After having watched Google I/O 2018, we decided to invest in supporting Instant App. In this article, we will cover the experience how we went from a monolithic project to multi-modules project and benefits Instant App brings to us.

L ive Message and our instant app concept

Before we take a deep dive, we’ll tell you something about our product. Use Live Message feature to draw your own emojis or write handwritten messages on photos then make animated GIFs. Technically speaking, what drawn on the canvas is recorded and play back. Because we want users to get acquainted and play with the app first, instant app is an opportunity for us to offer users an easy way to install our main app.

Structure & plan

After deciding to develop an instant app we sat down and started brainstorming how to divide modules. The app at the time mainly had one module. We changed this to a base feature and extracted all necessary code into specific modules. Our modules would look like this:

base : base feature containing core drawing code

: base feature containing core drawing code export : code as an entry point to use base module with export GIFs native library

: code as an entry point to use module with export GIFs native library preview : another entry point without export engine

: another entry point without export engine installed : installable app build.gradle

: installable app build.gradle instant : instant app build.gradle

Pink block is future plan

It was time-consuming to pull out code and resources out of the base feature. During this whole process of extracting code the project wouldn’t always compile. We were working through different compile errors and runtime errors until we got a stable version. One important thing to note that if you reference to resources from the base module, you have to import R as feature package. So com.example.app.R would become com.example.app_feature.R for example. Importing wrong R package leads to clueless compilation error message.

Dagger

Getting Dagger to work was most frustrating part. We have to provide a custom ActivityInjector to make this work for Dagger Android. The problem is that the default implementation relies on the Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> to be known statically when the application is created. That isn't the case for InstantApps because not all activities are loaded yet. We found a few feasible solutions. The best one we chose looks like

Dagger code for a single feature

Unsupported features

We couldn’t find a list of features that AIA doesn’t support. We ran into some cases we noted below

Access to Settings class

java.lang.SecurityException: ContentProvider request not available to instant apps: settings:GET_global

2. Query from ContentResolver

java.lang.NullPointerException: Attempt to invoke interface method ‘boolean android.database.Cursor.moveToFirst()’ on a null object reference

3. Runtime permission

IActivityManagerProxy : Instant app: com.ctech.livemessage crashed: java.lang.SecurityException: Not allowed to start activity Intent { act=android.settings.APPLICATION_DETAILS_SETTINGS

Fabric

To enable Crashlytics for all feature modules, we need to set up for the base module.

base/build.grade:

android {

...

} crashlytics { instantAppSupport true } dependencies {

...

}

You can use Fabric’s custom keys feature to add instant app information to your reports. This information can be very helpful while reading your reports afterwards. To do that, set a boolean value while initializing Fabric:

private fun initCrashlytics() {

Fabric.with(this, Crashlytics())

Crashlytics.setBool("InstantApp",

InstantApps.isInstantApp(this))

}

One takeaway lesson is that you have to apply plugin: 'io.fabric' for both base and installed module. We encountered a serious issue with this mistake. Our users emailed us to complain about a new update with constant crash. It stroke us odd that we didn’t receive any warning from Crashlytics. We debugged by enabling Crashlytics for debug build and got an error below



install an Android build tool and ask a team member to invite you to this app’s organization. Caused by: io.fabric.sdk.android.services.concurrency.UnmetDependencyException: This app relies on Crashlytics. Please sign up for access at https://fabric.io/sign_up install an Android build tool and ask a team member to invite you to this app’s organization.

App Links

We thought after fixing above issues, we could be published on Play Store. However, before we actually got to a successful upload we ran into multiple issues with our app.

1. You should have at least one active APK that is mapped to site ‘livemessageapp.com’ via a web ‘intent-filter’.

It was fixed by adding url for the installable APK. It meant we added an url for export module.

2. You must provide a default URL for your instant app APKs

This is a must. Your instant APK must contain a default URL. Adding

<meta-data

android:name="default-url"

android:value="https://livemessageapp.com/preview"/>

to the preview module solved the problem.

3. Your site 'www. livemessageapp.com ' has not been linked through the Digital Assets Link protocol to your app. Please link your site through the Digital Assets Link protocol to your app.

This error was the most annoying. After uploading assetlinks.json to the host, the error still occurred. We didn’t know what went wrong. After a few hours researching, here are the things we learn

Using app signing key over upload key

over Although we passed the test, it didn’t work still.

We tried to use web API presented in this link to debug. It threw us a clueless error message which cannot even find in the doc. Fortunately, we found a great tool for this issue. In our case, our livemessageapp.com was redirected to www.livemessageapp.com. If you use Firebase, it’s an easy fix

Proguard

We had silence crashes after having made it to Play Store. It failed before reaching to Crashlytics setup. We enabled Proguard for debug build and encounter a log

java.lang.RuntimeException: Unable to instantiate activity ComponentInfo: java.lang.ClassNotFoundException: could not find com.xxx.LiveDrawingPreviewActivity in any atom class loader or parent class loader

It turned out that Proguard doesn’t work for multi-feature instant app until Android Studio 3.2 Canary 14. At the time of this writing, we use AS 3.2 beta 1. Everything works just fine.

A side note after upgrading to build tool 3.2 beta 1

Previously instant app version was marked by base and feature module. It now relies on installed module regardless.

Conclusion

Building instant app was not a one way win for us, it has benefits beyond instant app. Our project structure was expanded to a way that made it more modular and efficiently organized. It would make more senses for us to integrate AR mode as dynamic feature.

These changes and improvements did not only make instant app possible, but also brought a lot of value to and refreshed the look of the Live Message app.

We hope you like our developer story. Please let us know what you think! There are more developer stories coming.

Try it at

https://livemessageapp.com/preview

References

https://stackoverflow.com/questions/46231746/multi-feature-instant-apps-cant-be-proguarded

https://github.com/felipehjcosta/kotlin-rxjava-android