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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Instant Apps: Getting Started
30 mins
- Getting Started
- How Instant Apps Work
- App Bundles
- Your First Instant App
- Adding Instant Functionality
- Testing Instant Apps
- App Links
- Verifying App Links
- Implementing App Links
- Testing App Links
- Using Multiple Entry Points
- Specifying the Default Entry Point
- Additional Options
- Prompting the User to Install
- Check Whether the App Is Running as an Instant App
- Where to Go From Here?
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.
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.
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.
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.
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.
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.
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:
- Build your app as an App Bundle.
- Create an .apks archive for your device from the bundle.
- 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.
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:
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:
- Set where your app bundle file .aab is located.
- Set the destination for the .apks file’s output.
- Indicate that bundletool should only generate APKs for the devices connected to your development machine.
- Provide the keystore path.
- Provide the keystore password.
- Provide the keystore key alias.
- 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:
- Project.afterEvaluate executes code after evaluating the project.
- Android.applicationVariants returns a collection of all build variants after evaluating the project.
- 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.
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:
- WorkingDir is a property of the Exec task type. As its name suggests, it specifies the working directory for the executed command.
- CommandLine specifies the command to execute.
- 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.
- The second doFirst block deletes the results folder if it exists. This is the first block executed in this task.
- 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:
- An @Input annotation, which specifies that the property is an input property for the task.
- An @Option annotation, which specifies the option name and description.
- AndroidSdkPath, which specifies the path to Android SDK.
- ApksPath, which specifies the path to the .apks file generated in the previous step.
- An @Optional annotation, which specifies that the input property is optional.
- 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
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.
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" />
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:
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.
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!
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development — plans start at just $19.99/month! Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more