Make Your App Instant

A week ago Google officially announced the release of Android Instant Apps and opened the API to all developers.

Instant Apps is a bridge between mobile web and native apps. It absorbs the best of both worlds and provides a unique user experience. Instant Apps was designed with speed, light weight, best UX and modularity in mind. Just like mobile web apps Instant Apps is very light and does not require installation. On the other hand, it provides a truly native user experience with material design and native navigation, which users are so used to.

Jet.com’s Android team was one of Google’s early partners with access to Instant Apps API and development tools at the experimental stage. Building our first Instant App has been an amazing journey, which I am very excited to share.

This article will not cover Instant Apps basics, as Google’s documentation provides an excellent introduction. Instead, it will focus on specific tips how to adapt your installed app to Instant Apps project structure, use the same codebase, slim down your app and break it into features. The article will also describe some issues the Jet Android team ran into during the development process and will discuss how building Instant Apps improved our installed app.

Single Feature vs Multi Feature Instant Apps

The official documentation mentions two ways to build Instant Apps: Single Feature and Multi Feature Instant Apps. The difference between the two approaches is that the former lets you build Instant Apps with only one module (Base Feature Module), while the latter requires a lot of modularization, refactoring and building a separate module per app feature.

If your current app is relatively small in size, Single Feature Instant Apps may seem to be a good way to start. It is pretty easy to build an Instant App within a short period of time as this approach does not involve a lot of refactoring. Building a Single Feature Instant App requires 3 main steps:

  • convert App Module of the installed app to Base Feature Module. It should contain most of the code and resources of a regular APK.
  • add a new Android Instant Apps Module, which does not have any source files. The module is responsible only for packaging Base Feature Module into Instant Apps zip bundle.
  • add a new App Module (APK) module, which builds an installed APK.

As you can see, converting your Android app to a Single Feature Instant App involves just adding new modules and changing mainly gradle plugins. For more information on building Single Feature Instant Apps, please see Google’s documentation.

While building Single Feature Instant Apps can be quick, it is not the most efficient and scalable approach. Instant Apps has a size constraint, which is 4MB total download limit. Your regular APK may be relatively small at the time when you start building Instant Apps and you may meet the size limit requirements. But you will most likely run into a size issue once you keep building your app and adding new features and libraries.

In order to mitigate the risk and ensure no size issues will arise in the future, it is better to start with Multi Feature Instant Apps approach from the beginning. It may be a bit time-consuming, but is proven to be more efficient in the long run. This is the path the Jet Android team has decided to take and stick to after facing size issues with Single Feature Instant Apps.

In addition to the size problem, the Jet team also encountered some challenges caused by the project structure of the installed APK. Our app was not modularized and was structured around one monolithic app module, which is an Instant Apps anti-pattern. To remedy this, we first identified the main use cases of the Jet Instant App and entry points into our application. Later we started to clean classes and move them into corresponding feature packages. We ended up breaking the app into 5 Feature Modules.

Jet Instant Apps project structure

As it appears on the screenshot, there is a baseLib package, which represents our Base Feature Module. It contains the app networking layer, analytics and account managers, A/B testing controllers, etc. Everything that is shared among other modules and reused across the app should be located in the Base Feature Module. There are also 5 Feature Modules, each of which is built around a specific feature:

  • plpLib shows a list of products
  • pdpLib shows a specific product
  • cartLib takes care of cart logic
  • checkoutLib handles checkout process
  • orderDetailLib summarizes the user’s order

You must also have noticed a miscLib package. It contains code and resources specific only to the installed app, which are not used in our Instant App. A miscLib is not pulled as a dependency in Instant Apps.

The other two modules (app and jetia) are mostly empty. They don’t have any code or resources and are mainly responsible for packaging base and feature modules into either installed APK or Instant Apps zip bundle with feature APKs.

A few weeks of intense refactoring and re-architecture work gave our project a totally new look. The chart below displays a new project structure that is shared by both installed APK and Instant Apps.

Jet Instant App with 5 feature modules and a base feature module.

Practical tips for reducing app size

Instant Apps has 4MB total download limit, which seemed to be unreachable when considering that Jet Android app is about 3 years old with a lot of complex features and heavy dependencies. I would like to point out that 4MB size limit refers to the sum of the base module and a feature module on the first download. A developer cannot know in advance which feature would be pulled first with the base module to a user’s device, because there are many entry points into Instant Apps. Thus the developer needs to make sure that each feature module together with the base module would not exceed 4 MB in total.

Here is a list of practical tips on how to slim down an app and reduce its size:

  1. Review dependencies

Start off with reviewing app dependencies. Check out every library included in the app and make a list of the ones Instant Apps does not require or can function without. For example, Jet’s installed app has a credit card scanning SDK Card.io, which adds over 2.8 Mb to the app size. We removed the library from our Instant App because Google Payments handles the payment process, making credit card scanning unnecessary for checkout process.

Using the same codebase you can create Instant Apps as a separate flavor or build type, thus controlling which dependencies you need to keep as part of installed APK but remove from Instant Apps.

Reconsider all marketing and analytics libraries integrated in the app. Jet’s installed app has 4 marketing and 3 analytics libraries, which provide overlapping functionality. We removed all but one from our Instant App. We then applied the same evaluation to crash reporting SDKs.

In order to list dependencies you can run a useful Gradle wrapper command line, which prints out the dependency tree, including all transitive libraries

./gradlew app:dependencies

In addition, there are many tools that can provide detailed information on library specs and weight. We used Method Counts, which shows not only JAR size, but also method count of a dependency.

2. Remove features not supported in Instant Apps

Google created a list of features that are not supported in Instant Apps. It is reasonable to remove the code and exclude any dependencies related to those features. For example, Jet’s installed APK has support for push notifications, which was dropped in Instant Apps. Removing code files and a dependency on Appboy SDK gained us a lot in terms of slimming down the app size.

3. ProGuard the app and shrink resources

We all know what a headache ProGuard maintaining can be, but the tool itself can be of a huge help when reducing Instant Apps size. ProGuard detects and removes unused classes, fields, methods, and attributes, including those from included code libraries. In addition to shaving off unused classes and methods it is useful to shrink resources as well. Be aware that resource shrinking works only with ProGuard enabled. Below is a code snippet to turn on ProGuard and resource shrinking:

android {
...
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

It is of a note that Instant Apps might require adding extra ProGuard rules to your project. When working with Instant Apps, ProGuard is not smart enough to detect that some classes and methods are used between different feature modules. As a result, it strips out a lot of necessary code. So when you see something similar to the exception below,

                                           java.lang.NoSuchMethodError: No virtual method setDisplayShowTitleEnabled(Z)V in class Landroid/support/v7/app/ActionBar; or its super classes (declaration of 'android.support.v7.app.ActionBar' appears in /data/user/0/com.google.android.instantapps.supervisor/.../base.jar)

you need to add an extra ProGuard rule to keep the class:

-keep class android.support.v7.app.ActionBar { *; }

4. Remove PNGs

PNG files are heavy resources and add a lot to Instant Apps size. APK analyzer tool provides a clear picture of how much your images increase the app size. Converting PNGs to WebP format helped us to reduce the app size significantly. This can be easily done in Android Studio. Right click on a PNG resource brings up a dialog with an option to Convert to WebP.

Android Studio menu option to convert a resource file to WebP.

On top of that, it is often a great idea to remove support for densities that are not often used in Instant Apps.

5. Use Vector Drawables

Using vector drawables instead of bitmaps is more efficient in terms of space saving because the same file can be resized for different screen densities without loss of image quality. Android Studio has Vector Asset Studio — a tool to import icons as vector drawable resources. To enable support for vector drawables on Pre-Lollipop devices the below flag should be added to the build.gradle file:

android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}

Results

Building Instant Apps was not a one way win for us, it has benefits beyond Instant Apps. A new Instant Apps project structure was expanded to the Jet installed app, which made it more modular and efficiently organized. By enabling ProGuard, cleaning up resources and removing some 3rd party libraries, we have significantly reduced the overall size of the app. The initial size of the Jet installed APK was 13 Mb. After building our Instant App, it has been reduced to 6 MB.

Instant Apps enforces some new features and policies. Smart lock integration was an important prerequisite to build Instant Apps, as it provides seamless account signing in. This feature got integrated into the Jet installed app as well and has become one of the most favorite among our users. On top of that, Google Payments API simplified the app checkout process in general and made the experience smoother and more frictionless.

These changes and improvements did not only make Instant Apps possible, but also brought a lot of value to and refreshed the look of the Jet installed app.