Google recently released Instant Apps to developers as part of an effort to help kickoff the next big enhancement to the native app experience in Android. Instant Apps aim to help bring users into the best native app experience as quickly as possible by downloading only parts of an application when they need them. This makes it fast and easy to engage users with great mobile app experiences, even though they do not have the application installed on their devices.

Instant Apps are delivered to the user in small feature modules, each containing only the code and resources needed to accomplish a specific action. Instant Apps are triggered by URL intents, meaning that they can be started from anywhere including, search results, social media shares, messages, beacons, NFC and other applications or even other Instant Apps.

Instant Apps share a single code base with the installed apk counterparts and are also distributed via the Google Play Store through the Android Instant Apps section.

Developing an instant app

Tooling

In order to get started with Instant Apps, you’ll need to get a few new tools.

  1. Android Studio 3.0 and the Instant apps SDK - Along with the Instant Apps SDK, Google also announced the launch of Android Studio 3.0 Preview which comes with a lot of neat new features including Instant Apps support. You will need to download and install it in order to get started. The full set of instructions are available here

  2. Gradle 4.0 (Nightly) - Along with other improvements, Gradle 4.0 comes with new dependency configurations for you to use. It’s worth noting that the compile configuration was deprecated in Gradle 3.4 in favor of api and implementation. These new configurations help you control what dependencies you expose as part of your public API; Implementation should be used to declare dependencies that are available only internally to the module, while dependencies declared with api will be exported and made available downstream.

  3. Android Plugin for Gradle 3.0.0-alpha1 - This comes with a few new gradle plugins to help in modularizing your application; com.android.instantapp and com.android.feature.

  4. The Instant Apps API - Google has also made a handy collection of utilities which you can include in your project like so:

implementationcom.google.android.instantapps:instantapps:1.0.0

It contains a few useful static methods to help check if the user is interacting with the instant or installed version of your app, as well as prompt them with a system dialog to install the full APK.

Determine your use case

The first, and possibly most important, step is to determine what parts of your app will work best as features in an Instant App. Instant Apps are driven by actions and make themselves available to users right when they are needed. For example, a user at a new parking garage may not have the right parking meter app installed before hand, but with Instant Apps, all the user needs to do is visit a URL and the native app can take advantage of all the rich payment APIs already available, quickly and easily.

Instant Apps should focus primarily on helping users complete whatever task they set out to do with as little friction as possible, not drive full app installs.

Similarly, Instant Apps should not act as trial versions or lite versions of the full app. This may warrant reconsidering how your users currently interact with your apps. How do they come to visit the app? As well as what kind of content do they frequently share?

Consider what the new flow would be like. There could be multiple entry points into your application where none of them may ever be from the launcher. And you must also work with the possibility that the app may be evicted from the system right after the user is done interacting with it.

Slim down your application

With Instant Apps, having a lean app is more important than ever before.

Each feature of your Instant App must be under 4MB in size.

Which doesn’t really leave a lot of room for large dependencies. This means that it’s worth reconsidering what is important to you and your users. For example, you may be able to save on feature bloat by: reconsidering what your success factors are as well as how you track them; removing unused code; optimizing resources; and properly proguarding your modules can really help slim down your application.

Support deep linking and App links

If you have built complex apps that support multiple user flows, you will likely have implemented deep linking. Deep linking allows anyone to create a URL that links directly into a particular screen or flow in your app. Since Instant Apps run on URLs, deep links and app links are now a requirement. One major difference with regular deep links is that custom URI schemes are not supported such as

myshoppingapp://products/600613

Instead, you must now support https URLs

https://<my-shopping-app-domain>.com/products/600613

You may continue to use your custom scheme in your installed app if you wish, however there is a strong case to be made for switching all deep links to URLs.

Every feature in your Instant App must have at least one entry point that is defined as a deep link. This defines what Activity users will see when they click an Instant App URL, or if they navigate to the feature from a different feature in your Instant App. Here is an example of an intent filter that binds a deep link pattern to an Activity.

<activity
            android:name=".ui.ItemDetailActivity">
            <intent-filter
                android:autoVerify="true"
                android:order="1">
                <action android:name="android.intent.action.VIEW"/>

                <category android:name="android.intent.category.BROWSABLE"/>
                <category android:name="android.intent.category.DEFAULT"/>

                <data android:host="bumblebee.willowtreeapps.com"/>
                <data android:pathPattern="/item/.*"/>
                <data android:scheme="https"/>
                <data android:scheme="http"/>
            </intent-filter>
        </activity>

In this case, ItemDetailActivity will be launched when a user visits https://bumblebee.willowtreeapps.com/items/600613.

App Links

Secondly, you will also need to associate your web domain with your Instant App’s package name. This binding, known as Android App Links, proves to Google that you own and control the web domain that you wish to associate with your app. Previously, App Links allowed installed apps to automatically associate themselves with your web domain so that when users click on your website’s URLs, they skip the disambiguation dialog and are taken directly to your app. Now, by setting up App Links for your Instant App, users without your installed app will be routed seamlessly to your Instant App. App Links are optional for installed apps since users can manually choose what apps they want to handle your deep links, however App Links are a requirement for Instant Apps to work. To set it up, you simply need to host a single JSON file on the root of your domain or subdomain at <my-domain>/.well-known/assetlinks.json

[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls"
    ],
    "target": {
      "namespace": "android_app",
      "package_name": "com.myapp.packagename",
      "sha256_cert_fingerprints": [
        "96:14:26:30:CC:E3:C0:9B:05:12:7B:9A:31:9E:88:36:82:12:84:27:4C:52:2F:05:FE:66:A8:AB:B9:F0:F5:F0"
      ]
    }
  }
]

While your Instant App modules will need to have separate package names for namespacing purposes, your Instant App and installable app can share their application ID, so you only need to register that application ID in the "package_name" field. You can find out more information on setting up app links here.

Modularize and refactor your application

This is perhaps the most difficult step in putting together an Instant App especially for existing applications. This is because the vast majority of apps today are mostly single module builds while supporting Instant Apps requires developers to split their builds into multiple modules called features. Each feature represents a part of the application that can be downloaded on demand.

Feature module relationships

  1. Feature modules - are modules that applies the new com.android.feature Gradle plugin. They are simply library projects, producing an aar when consumed by other modules. It’s worth noting that they do not have application IDs because they are just library projects.
apply plugin: 'com.android.feature'
android {
    ...
}
dependencies {
    implementation project(':base')
    ...
}

The feature module’s manifest is also worth noting because since it is functionally only a part of the full app, its manifest will be merged with others when the project is assembled. So feature module manifests should contain things like Activities that are contained in the module. Each feature manifest should also contain a unique package name for the module.

For example here’s a manifest from a sample feature module.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.willowtreeapps.instantappsdemo.feature.browse">
    <application>
        <activity
            android:name=".ui.BrowseActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">
            <intent-filter android:order="1">
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.BROWSABLE" />
                <category android:name="android.intent.category.DEFAULT" />

                <data
                    android:host="bumblebee.willowtreeapps.com"
                    android:pathPattern="/"
                    android:scheme="https" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
  1. Base feature module - The base feature module can be thought of as the root of your project. It contains the shared code and resources to be used by other modules. The base feature differentiates itself from other features with the property baseFeature set to true.

Every project that uses feature modules must have exactly one base module and every feature module must depend on the base module.

Here’s a sample base feature module build script.

apply plugin: "com.android.feature"

android {
    baseFeature true
    ...
}

dependencies {
    feature project(“:feature”)
    application project(“:app”)
    ...
}

The base feature manifest would generally contain global stuff like your application tag. Here’s a sample base feature manifest.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.willowtreeapps.instantappsdemo">

    <application
        android:name=".ui.base.BaseApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name="com.willowtreeapps.instantappsdemo.feature.browse.ui.BrowseActivity"
            android:label="@string/app_name">
            <meta-data
                android:name="default-url"
                android:value="https://bumblebee.willowtreeapps.com/" />
        </activity>
    </application>

</manifest>

While not mandatory, it’s recommended that your base feature manifest contain an activity tag that references an activity that implements a default-url metadata. This tells Android which activity to launch in the event that your Instant App is not opened from a deep link but somewhere like a launcher instead.

  1. APK module - This is the normal build module that we are all familiar with. Now it’s setup to consume the base and feature modules in order to output an apk to be installed on user devices. Since it aims to output an installable artifact this module does have an application ID.
apply plugin: 'com.android.application'

android {
    defaultConfig {
        applicationId "com.willowtreeapps.instantappsdemo.apk"
        ...
    }
    ...
}

dependencies {
  implementation project(":my-base-feature")
  implementation project(":my-feature")
}

The application manifest here is the result of merging all of the other manifests that it inherits from the other feature modules. As a result its manifest is pretty sparse.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.willowtreeapps.instantappsdemo.apk">
</manifest>
  1. Instant App module - implements the com.android.instant plugin. The consume feature modules and produce a split APK zip containing all of the features that will go into the Instant App. It’s pretty much an empty shell of a project without a manifest that only implements other features feature modules in the project.

Here’s a sample of an Instant App module build script.

apply plugin: "com.android.instantapp"

dependencies {
    implementation project(":my-base-feature")
    implementation project(":my-feature")
}

Problems you may encounter

Now that we’ve looked at how Instant Apps are structured, it’s important to take a look at some of the problems that we ran across putting together an Instant App. Some gradle plugins that you rely on may not work. Many gradle plugins for Android projects check for modules using the com.android.application or com.android.library plugins. While the new com.android.feature plugin, works similarly to the library project, they do not have the sample package name so your favorite gradle plugins may need to be updated.

In supporting Instant Apps, it makes sense to use deep links for all of your in-app navigation and, in some cases, it’s necessary. If you are just now adding https deep links to your app (or switch away from a custom scheme) you may notice that Android pops up a disambiguation dialog when you navigate from one activity to another via deep link. The system needs the user to decide whether to handle the deep link in the web browser or your app. Obviously, you would want the user to stay in your app by default and not give them the choice of leaving to the web browser. This is solved by implementing App Links as described above. But if during development you don’t have your web server setup correctly yet, you can implement this workaround to avoid the disambiguation dialog:

private static void invokeDeepLink(Context context, String deepLink) {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(deepLink));
        intent.setPackage(context.getPackageName()); // direct to activities in this app
        context.startActivity(intent);
    }

You can get your app’s package name from a Context and force the VIEW Intent to only consider activities under your package name.

Deployment

Development Testing

To test your Instant App locally during development, you can obviously just use Android Studio to run your Instant App. But here’s how it works behind the scenes.

Let’s assume your instant-app module is name instantapp. First run the gradle task gradle :instantapp:assembleDebug This will produce a zip archive artifact in your builds folder. Next unzip this archive and you will find several APKs, one for each feature module. Finally, run this adb command to simultaneously install all of these APKs. adb install-multiple -r -t --ephemeral base.apk feature1.apk feature2.apk where base.apk, feature1.apk, and feature2.apk are your feature APKs. Warning: we have noticed that this adb command may fail intermittently.

Deploying to Production

In order to deploy your Instant App to the Google Play Store, you simply need to run the same gradle task as above but using the release variant: gradle :instantapp:assembleRelease And then upload the zip archive to the Play Store console. You do not need to unzip the archive. But before Google will accept your Instant App, you will need to ensure some things are setup correctly.

Code Signing

Instant Apps are essentially a bundle of APKs, one for each feature module. So you will need to sign each of these APKs the same way you sign your installable APK. This means you will need to add a signing config to each feature module’s build.gradle (including the base feature module).

Intent Filters

In order to deploy your Instant App, you will also need to verify the intent filters in your manifests are formatted a certain way. For an example of a working setup, see the <activity> declaration for the ItemDetailActivity above. Here are the key points:

Make sure you include android:autoVerify="true". This attribute tells Android to automatically verify your App Links. Since Instant Apps works off of App Links, this attribute is required. Make sure you use multiple <data> tags with only one attribute each. So instead of

<data
                 android:host="bumblebee.willowtreeapps.com"
                 android:pathPattern="/"
                 android:scheme="https" />

You should use

<data android:host="bumblebee.willowtreeapps.com"/>
<data android:pathPattern="/"/>
<data android:scheme="https"/>
<data android:scheme="http"/>

Notice above that we added a scheme declaration for http. Although Instant Apps only supports https URLs, your intent filters are required to handle both http and https.

Sample app: Bumblebee

We built a sample app, named Bumblebee, just to get a feel for what’s possible with Instant Apps. Bumblebee is a fictitious store with a simple catalog and shareable shopping carts. It uses Firebase for catalog data, user data, and resource hosting. We also architected the app using Google’s new Architecture Components, which we found to be tremendously helpful and easy to use. You can look here for a more detailed breakdown of these new architecture libraries we recommend checking out these awesome blog posts by Eric Richardson.

Lifecycles of the rich and famous

The viewmodel is nice from up here

Bumblebee is an Instant App with three feature modules, each containing their own screen. The root entry point is a Browse feature that shows off a grid of products available for purchase (actually just photos of items we found lying around the office). Tapping on one takes you to the Item Detail feature that lists a price and full description. From here, you can choose to add the item to your shopping cart. Once you have a cart, you can view it using the Shopping Cart feature and easily share an Instant App link to your cart. Remember, Instant App links are really just urls. Anyone who you share the link with can immediately access your cart directly as an Instant App without any need to download the catalog features.

Here are some links you can open on your Android phone to try out Bumblebee:

https://bumblebee.willowtreeapps.com/

https://bumblebee.willowtreeapps.com/item/0

You can invoke these URLs as intents via adb commands:

adb shell am start -a android.intent.action.VIEW -d "https://bumblebee.willowtreeapps.com/"

And you can check out how it’s built on our github repo here: https://github.com/willowtreeapps/android-instant-apps-demo/