Instant Apps: Getting Started

In this tutorial, you’ll learn how to integrate Google Play Instant with your Android project using Instant Development SDK. By Ivan Kušt.

3.7 (3) · 1 Review

Download materials
Save for later
Share

Imagine how great it would be if you could try out apps without even installing them. Well, you can! Google Play Instant enables you to do just that, and this tutorial will show you how to enable this feature for your own apps.

In this tutorial you’ll learn:

  • How to use Instant Development SDK to instant enable your apps.
  • What App Bundles are and how to use them to create instant apps.
  • What App Links are and how to use them in combination with instant apps.
Note: This tutorial assumes you know the basics of Android development with Kotlin. If you are new to Kotlin, check out our Kotlin introduction tutorial. If you are completely new to Android development, read through our Beginning Android Development tutorials to familiarize yourself with the basics.

Getting Started

Google Play Instant enables your apps to launch on devices running Android 5.0 (API level 21) or higher without having to install them first. Apps that run this way are called instant apps or instant games. The experience of running an instant app is referred to as instant experience.

Note: To use Google Play Instant, you’ll need to install Android Studio 3.2 or higher. If you don’t have it, please download it before continuing.

In this tutorial, you’ll practice using instant apps by working with a From Dusk Till Dawn app, which gives you information about important times of the day for any city.

To get started, download the From Dusk Till Dawn project using the Download Materials button at the top or bottom of this tutorial. Open the project in Android Studio 3.2 or later by selecting Open an existing Android Studio project on the Android Studio welcome screen:

In order to build an app that supports Google Play Instant, you need to install the Instant App SDK. Go to Tools ▸ SDK Manager, click on the SDK Tools tab and then install Google Play Instant Development SDK by checking the box and pressing Apply:

Once you have installed the SDK, build and run the project.

After installing the app, grant it permission to access your location:

You’ll see the main screen of the app, which shows the sunrise and sunset times for your location in UTC.

The app consists of two screens:

  • The main screen, which shows information for your current location.
  • The location details screen, which lets you search for information for a different location.

You can search for a location by typing in the city’s name and pressing the magnifier icon on your keyboard.

How Instant Apps Work

An instant app is a special version of your app that runs without the user needing to install it first. The idea is that you have a lighter flavor of your app that enables users to give it a try before they commit to installing it.

You can set up deep link URLs that launch specific activities in your instant app. A typical use for this would be to put different banner links on your site that launch the most relevant activity of your app, all without any installation!

There are some limitations, though. Google Play Instant recognizes two levels of instant experience, with a different app size limitation for each:

  • Basic instant experience: An instant app that launches from a Try Now button or a website banner must have an app size under 10MB.
  • Enhanced instant experience: An instant experience that launches from any surface that can launch a URL, such as messaging apps, email apps or ads, must have an app size under 4MB.

This means that you’ll want to provide a trimmed-down version of your app that showcases what the app can do.

Note: Users have to opt in to instant apps before using them. Here are the steps to opt into instant apps.

There are currently two approaches that you can use to create instant apps or to enable your current app to be instant-ready:

  • Feature plugins.
  • App Bundles.
Note: Google recommends using App Bundles. If you want to learn more about using feature plugins, you can check the official feature plugin documentation.

In this tutorial, you’ll learn how to use App Bundles to add an instant app experience to an existing app.

App Bundles

Android App Bundle, which uses the .aab extension, is the new delivery format that replaces .apk archives for publishing Android apps to the Google Play Store.

How does it work? App Bundle contains all of the compiled code and resources. When users request to download your application from Google Play Store, the store uses a new serving model called Dynamic delivery.

Dynamic delivery generates and serves an .apks archive optimized for the user’s device. That way, the download includes only the code and resources it really needs, which results in a smaller application size and a smaller download. An added benefit is that you no longer have to manage multiple .apk files to support different devices.

Note: For more information on App Bundles, check out our tutorial on Getting Started With App Bundles.

Your First Instant App

Now that you know how Instant apps and App Bundles work, you’re ready to make your first instant-enabled app!

The first thing you’ll need to do is add a new flavor that contains an instant version of the app.

Open the build.gradle file in the app folder and add a new flavor dimension to determine whether you are using an instant or an installed version of the app:

flavorDimensions "experience"
productFlavors {
  instant {
    versionCode 1
  }
  installed {
    versionCode 2
  }
}

This defines two new flavors in the experience flavor dimension:

  • The instant flavor, which contains the instant version of the app.
  • The installed flavor, which contains the installed version of the app.

One very important thing to note is that the code size of the instant flavor version must be smaller than the code size of the installed flavor version. This allows users to upgrade from the instant app to the installed version of the app.

Make sure everything still works by building and running the app.

Adding Instant Functionality

The next step is to create a new folder named instant in the app/src folder.

In the new folder, create an AndroidManifest.xml file with the following contents:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:dist="http://schemas.android.com/apk/distribution"
  package="com.raywenderlich.android.fromdusktilldawn"
  android:targetSandboxVersion="2">

  <dist:module dist:instant="true"></dist:module>
</manifest>

You’ll immediately notice a few differences from the usual manifest:

  • The android:targetSandboxVersion property.
  • The dist:module tags.

These settings will place your app in a more secure SELinux (Security-Enhanced Linux) sandbox. This lets the instant version of the app transfer data to the installed version with a higher level of security.

Note: For more information, check the official SELinux sandbox documentation.

Select the instantDebug build variant.

Build and run the app again to make sure there are no errors.

You’ll get a prompt from Android Studio because you’re running a different version of the app. Answer the prompt with OK:

You’ll still get the installed version of the app, despite the error message.

Running instant apps using App Bundle is still not seamless in Android Studio, but don’t worry about it!

Your next task is to set up a way to easily run and test instant apps.

Testing Instant Apps

To run and test your instant app on a device or an emulator, you’ll have to go through a few steps:

  1. Build your app as an App Bundle.
  2. Create an .apks archive for your device from the bundle.
  3. Use ia tool to launch the instant experience on your target device or emulator.

Here’s how these steps will work.

For the first step, you’ll use Android Studio or the gradle command line tool to generate an App Bundle for the instantDebug flavor.

Note: Check out our App Bundles tutorial for details on how to generate App Bundle.

To create and extract the .apks archive from App Bundle, you’ll use a tool called bundletool. This is a tool developed by Google which Google Play Store uses when delivering an app to your device.

Go to the bundletool release page and download the latest version of bundletool-all-*.jar file into your project root directory:

Note: To proceed, you need to generate the keystore for your app. Take a look at this tutorial to find a step-by-step guide. You’ll need to remember the keystore password, the key alias, and the key alias in order to continue to the next step.

Here’s how you call bundletool to generate an .apks archive:

$ java -jar bundletool-all-*.jar build-apks \
    //1
    --bundle=app.aab \
    //2
    --output=app.apks \
    //3
    --connected-device \
    //4
    --ks=your-keystore-path \
    //5
    --ks-pass=pass:your-keystore-password \
    //6
    --ks-key-alias=your-key-alias \
    //7
    --key-pass=pass:your-key-password

In this command line, you:

  1. Set where your app bundle file .aab is located.
  2. Set the destination for the .apks file’s output.
  3. Indicate that bundletool should only generate APKs for the devices connected to your development machine.
  4. Provide the keystore path.
  5. Provide the keystore password.
  6. Provide the keystore key alias.
  7. Provide the keystore key password.

You’ve seen just a small set of things you can do with bundletool. You can find more in the complete bundletool reference.

Finally, find the command line tool for launching the app, called ia, in the Android home folder, typically found in the extras/google/instantapps folder. To run the .apks archive on your device as an instant app, you would run the following command in a terminal:

$ANDROID_HOME/extras/google/instantapps/ia run app.apks

This process happens when users run instant apps published on Google Play.

When developing an app, it would be tedious to repeat all of those steps each time you want to run your instant app. So the next thing you’ll do is create gradle tasks that’ll do the grunt work for you!

There are already tasks in place to generate App Bundles for all build variants. Your next job is to create two additional gradle tasks for the second and third steps.

Open build.gradle file in the app module. Add the following code at the end of the file, after the dependencies section:

//1
project.afterEvaluate {
  //2
  android.applicationVariants.all { variant ->
    //3
    task "assembleSplits${variant.name.capitalize()}"(type: Exec, dependsOn: "bundle", group: "build") {
    }
  }
}

This will create a new task that extracts a set of .apks archives for each build variant. Take a look at this task, step by step:

  1. Project.afterEvaluate executes code after evaluating the project.
  2. Android.applicationVariants returns a collection of all build variants after evaluating the project.
  3. Task method creates a new task that depends on the “bundle” task for each build variant.

One more important thing to note is that new tasks are of type Exec, which is a convenience task used for calling command line tools.

Note: For more information, check the documentation on Exec tasks in gradle.

The next thing to do is to call bundletool from the task.

Bundletool needs the following arguments:

  • A bundle to generate from.
  • A keystore path.
  • A keystore password.
  • A keystore key alias.
  • A keystore key password.

Create a helper method for fetching the signing configuration for the build variant by adding the following code at the start of project.afterEvaluate:

ext.getSigningConfig = { variant ->
  return variant.signingConfig ?: variant.buildType.signingConfig
}

The next thing you need to do is to tell the task where to find bundletool. You’ll use the local.properties file in project root. Open the local.properties file and add the following line:

bundletool=bundletool-all-*.jar

The new bundletool property specifies the path to the bundletool .jar file relative to project root folder.

Add the following code right after the getSigningConfig() method definition:

Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def bundletool = properties.getProperty('bundletool')

This will read the property named “bundletool” from the local.properties file.

Now that you have everything you need, paste the following code inside the task:

//1
workingDir project.rootDir
//2
commandLine = ["java", 
               "-jar", bundletool, "build-apks", 
               "--bundle=${project.buildDir}/outputs/bundle/${variant.name}/app.aab",
               "--output=${project.buildDir}/outputs/splits/${variant.name}/app.apks",
               "--connected-device",
               "--ks=${getSigningConfig()?.storeFile}",
               "--ks-pass=pass:${getSigningConfig()?.storePassword}",
               "--ks-key-alias=${getSigningConfig()?.keyAlias}",
               "--key-pass=pass:${getSigningConfig()?.keyPassword}",
               "--adb=${android.getAdbExe()}"
]
//3
doFirst {
  mkdir "${project.buildDir}/outputs/splits/${variant.name}"
  println "Running bundletool:\n${commandLine.join(" ")}"
}
//4
doFirst {
  delete "${project.buildDir}/outputs/splits/${variant.name}"
}
//5
doLast {
  println "Creating splits for ${variant.name}"
}

Here’s how this process works, step by step:

  1. WorkingDir is a property of the Exec task type. As its name suggests, it specifies the working directory for the executed command.
  2. CommandLine specifies the command to execute.
  3. The first doFirst block puts the code to execute at the beginning of the tasks queue. First block ensures that the folder for storing the results already exists.
  4. The second doFirst block deletes the results folder if it exists. This is the first block executed in this task.
  5. The third doLast puts the code to execute at the end of the task’s queue. This will simply print to the console the name of the variant split generated.

Run the task, assembleSplitsInstantDebug, from the console. It checks that you have an app.apks file in app/build/outputs/splits.

Sync the project to make sure there are no errors.

Congrats! You’re all set for the next step!

The last step is to add tasks to run your instant app from the generated .apks. Those tasks will depend on the tasks you’ve created in the previous step.

First, you’ll create a new task type called ExecInstantApp that extends the existing Exec task. Add the following code to the end of build.gradle:

import org.gradle.api.tasks.options.Option

class ExecInstantApp extends Exec {
  //1
  @Input
  //2
  @Option(option = "androidSdk", description = "Android SDK path")
  //3
  String androidSdkPath

  @Input
  @Option(option = "apksPath", description="App apk splits path")
  //4
  String apksPath

  @Input
  //5
  @Optional
  @Option(option = "url", description = "Url to launch Instant App")
  //6
  String url

  @Override
  protected void exec() {
    if(url != null) {
      commandLine = [
          "${androidSdkPath}/extras/google/instantapps/ia", 
          "--debug", "run", apksPath, "-u", url
      ]
    } else {
      commandLine = [
          "${androidSdkPath}/extras/google/instantapps/ia", "--debug", "run", apksPath
      ]
    }

    println "Running ia:\n${commandLine.join(" ")}"

    super.exec()
  }
}

Take a look at what that code does. You’ve added three new properties and three annotations to the task:

  1. An @Input annotation, which specifies that the property is an input property for the task.
  2. An @Option annotation, which specifies the option name and description.
  3. AndroidSdkPath, which specifies the path to Android SDK.
  4. ApksPath, which specifies the path to the .apks file generated in the previous step.
  5. An @Optional annotation, which specifies that the input property is optional.
  6. Url, which is an optional property to specify the URL that launches the instant app.

The original exec() method from the Exec task executes the command which the commandLine property specifies. You’ve overriden it to set commandLine property to depend on the url property.

Sync the project to make sure there are no errors.

Now you can add the tasks for running all build variants as instant apps. Add the following code at the end of the project.afterEvaluate block that you added earlier.

  android.applicationVariants.all { variant ->
    task "runinstant${variant.name.capitalize()}"(type: ExecInstantApp, dependsOn: "assembleSplits${variant.name.capitalize()}", group: "build") {
      workingDir project.rootDir
      androidSdkPath = android.sdkDirectory.path
      apksPath = "${project.buildDir}/outputs/splits/${variant.name}/app.apks"
    }
  }

This creates a new task named for each build variant, like runinstantInstantDebug, for example. Tasks are of type ExecInstantApp and depend on tasks for generating .apks files.

You’ve already done the work for running the ia command in the task, so it’s just a matter of setting the working directory, the SDK path and the .apks file path.

Sync the project again to make sure there are no errors.

You’re almost ready to run your app as an instant app! There is one more important thing to do before you can, however: Delete the FromDuskTillDawn app from the device. Otherwise, ia won’t be able to run the instant app since the installed version has higher a versionCode than the instant app.

Now run the runinstantInstantDebug gradle task:

You’ve run the app as an instant app! Even though it currently looks the same, when you exit the app you’ll notice that there is no app icon in the launcher. So you did it! Great job!

App Links

The next thing you’ll do is create an App Link to your new instant app.

What are App Links exactly?

They are a special type of deep links for Android. They allow your website URLs to open your app without the user needing to select the app.

Using App Links, you can create banners on your website that launch your instant app! Nice, right?

There are two steps to creating App Links:

  • Verify the ownership of the app and website URL.
  • Add a deep link to a place in your app.

Verifying App Links

To verify your deep link to make it a valid App link you need to perform the following steps:

  • Request an automatic App Link verification in your AndroidManifest.xml file.
  • Host a Digital Asset Links JSON file on your domain to declare a relationship between your website and the app.

You must host the Digital Asset Links JSON file on your domain in the following location:

https://domain.name/.well-known/assetlinks.json 
Note: For more information and file format specifications, check the official digital asset links documentation. There is also official documentation on verifying app links.

Implementing App Links

You’ll need to add a deep link in the AndroidManifest.xml you added in instant package. This is done by adding a second intent-filter element to the activity element for the MainActivity. You can see the code below:

 <activity
    android:name="com.raywenderlich.android.fromdusktilldawn.ui.main.MainActivity"
    android:theme="@style/AppTheme.Splash">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <intent-filter android:autoVerify="true">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.BROWSABLE" />
      <category android:name="android.intent.category.DEFAULT" />

      <data
        android:scheme="http"
        android:host="example.com"
        android:pathPrefix="/main" />

      <data
        android:scheme="https" />
  </activity>

The android:autoVerify="true" property in the intent-filter declares that the deep link was verified.

Note: You have to add both Http and Https schemes in order for App Links to work. For more details, visit the official deep linking documentation.

Testing App Links

To test your new App Link, you’ll use the gradle task that you defined earlier. Run the runinstantInstantDebug task with a url parameter set to http://example.com/main.

Using a gradle wrapper, run the task with the following command:

On Mac or Linux:

./gradlew runinstantInstantDebug --url=http://example.com/main

On Windows:

gradlew runinstantInstantDebug --url=http://example.com/main

After this command builds successfully, the app will open the instant app:

Using Multiple Entry Points

You can add even more App Links to your instant app.

Start by adding another App Link to LocationDetailsActivity. Open the AndroidManifest.xml file and add a new intent-filter to LocationDetailsActivity:

<activity android:name=".ui.locationdetail.LocationDetailActivity" >
  <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.BROWSABLE" />
    <category android:name="android.intent.category.DEFAULT" />

    <data
      android:scheme="http"
      android:host="example.com"
      android:pathPrefix="/location" />

    <data
      android:scheme="https" />
  </intent-filter>
</activity>

This will add a new App Link that opens LocationDetailActivity with this url:

http://example.com/location?lat=45.8150&lng=15.9819

There is one more change you need to make: Open the LocationDetailViewModel.kt file and replace line 61 with the following:

val coordinates = if(intent.data != null) {
    Coordinates(
    intent.data?.getQueryParameter("lat")?.toDouble() ?: 0.0,
    intent.data?.getQueryParameter("lng")?.toDouble() ?: 0.0
    )
  } else {
    intent.extras?.get(COORDINATES_ARGUMENT) as? Coordinates
  }

This will parse lat and lng parameters passed with the App Link. You know that App Link opened the activity if intent.data is not null.

You can now test the new entry point by executing the following in the command line:

./gradlew runinstantInstantDebug --url="http://example.com/location?lat=45.8150&lng=15.9819"

Awesome work! You’ve now successfully created an App Link with multiple entry points!

Specifying the Default Entry Point

If you have multiple App Links, there are two ways to specify which one will be used as an entry point when users click Try Now in the Google Play Store:

  • Leave your default activity as an entry point for the Try Now experience.
  • Add URLs to activities and declare a default URL, which then becomes the entry point for the Try Now experience.

You can specify the default URL by adding the following in AndroidManifest.xml inside the application tags:

<meta-data android:name="default-url"
  android:value="https://example.com/location" />
Note: There is a new way to test this using ia tool, but Google Play Store uses the code above when users select the Try Now option.

Additional Options

There are some more options provided by the InstantApps API that are worth mentioning:

  • You can prompt the user to install the full version of the app.
  • You can determine whether the app is running in instant or installed mode.

Prompting the User to Install

The user tried your new instant app, but what now?

Well, you want them to install the full version of the app, of course!

There’s a simple way to prompt users to install the full version of the app from the instant app, and here’s how.

You’ll prompt users to install the full version of your app by using InstantApps APIs. They provide a convenient method called showInstallPrompt(), which shows an install dialog.

First, open the build.gradle file for the app module. Add the InstantApps dependency:

implementation 'com.google.android.gms:play-services-instantapps:16.0.1'

Sync the project with the gradle files.

Open the MainActivity.kt file and add the following code to the beginning MainActivity class:

  /**
   * Intended to launch after the app has been installed.
   */
  private val postInstallIntent = Intent(
      Intent.ACTION_VIEW, 
      Uri.parse("https://example.com/location")
  ).addCategory(Intent.CATEGORY_BROWSABLE)
   .putExtras(Bundle().apply {
      putString("The key to", "sending data via intent")
    })

You’ve defined postInstallIntent, which will be passed to the newly installed app the first time it’s run.

Add one more field to MainActivity:

private var askedToInstall = false

This serves as a boolean flag to let you know if you’ve already asked the user to install the full version. You don’t want to be too eager and bore the user.

Next, go to the searchForLocation() function and change its body to the following:

if (!askedToInstall) {
  askedToInstall = true

  InstantApps.showInstallPrompt(this,
    postInstallIntent,
    REQUEST_CODE,
    REFERRER)
} else {
  viewModel.searchFor(locationName).observe(this, Observer { coordinates ->
    if (coordinates != null) {
      startActivity(LocationDetailViewModel.createIntent(this, coordinates))
    } else {
      AlertDialog.Builder(this)
          .setMessage(R.string.cant_find_given_location)
          .setPositiveButton(R.string.ok, null)
          .show()
    }
  })
}

Next, add the following import:

import com.google.android.gms.instantapps.InstantApps

And define the following constants in the companion object:

companion object {
  private val REFERRER = "InstallApiActivity"
  private val REQUEST_CODE = 7
}

This defines the referrer and the request code that have to be passed to the showInstallPrompt() function.

Build and run the app using the runinstantInstantDebug gradle task.

This will show an install prompt one time when the user searches for a location:

Note: Hmm, the prompt looks kind of strange. That’s right! You’ll have to upload the app to Google Play Store to see a valid prompt.

Check Whether the App Is Running as an Instant App

One more useful thing to know is if your app is currently running as instant app. This feature lets you disable functionality that won’t work due to instant app limitations.

To perform the check, you use the following InstantApps API:

InstantApps.getPackageManagerCompat(this).isInstantApp

Open MainActivity.kt again and replace lines 70 and 71 with:

if(!InstantApps.getPackageManagerCompat(this).isInstantApp) {
  tvNoon.text = formatTimeString(this, R.string.noon_format, sunTimetable?.noon)
  tvDayLength.text = formatTimeString(this, R.string.day_length, sunTimetable?.dayLength)
}

This will fill the TextViews for noon and day length only if the app is running as installed app.

Go to LocationDetailsActivity.kt and replace lines 63 and 64 with:

if(!InstantApps.getPackageManagerCompat(this).isInstantApp) {
  tvNoon.text = formatTimeString(this, R.string.noon_format, sunTimetable?.noon)
  tvDayLength.text = formatTimeString(this, R.string.day_length, sunTimetable?.dayLength)
}

This does the same for the activity that displays data for a location searched for on MainActivity.

Now build and run the app as an instant app again.

You can see that the information about noon time and day length is no longer displayed either on the main screen or when you search for a location:

Now run the installed app:

In the installed version, you can see all of the information and you won’t get the install prompt.

Note: Also make sure to check the InstantApps API reference

Where to Go From Here?

That’s it! You’ve made your very first instant app! :]

Now that you know how to add instant functionality to your apps, make sure you check out the official UX best practices.

For step by step details on publishing Instant apps, check out Google Play Console help.

For more details on related topics not covered here, visit the official documentation on getting started with Instant Apps and check out how to add instant functionality to Android games.

I hope you have enjoyed this introduction to Instant Apps. If you have any comments or questions, please join the forum discussion below!