Google Play Instant enables native Android apps to run without requiring installation. Apps which support Google Play Instant can run in a variety of situations, such as when a user clicks a link in a text message or uses the "Try Now" button from the Google Play Store:
In this codelab, you will transform an existing Android project called "Topeka" into a project that supports Google Play Instant.
Ensure you are using Android Studio 3.0. If you are using an older version of Android Studio, download version 3.0 or later.
To transform "Topeka" you will start with unmodified source code for this app. To get the code, clone the repo using the following command:
$ git clone -b start https://git@github.com/googlecodelabs/android-topeka.git
Alternatively you can download the repository as a Zip file:
Let's open "Topeka" using Android Studio. Run Android Studio 3.0 or higher and choose "Open an existing Android Studio project". Open the Topeka project you fetched in the previous step.
Now let's ensure that everything builds correctly within Android Studio. Go to Build -> Build APK. The project should complete the build without any issues:
In order to build an app that support Google Play Instant, we need to install the Instant App SDK. Go to Tools -> SDK Manager. Click on the "SDK Tools" tab and install "Instant Apps Development SDK" by checking the box and hitting "Apply"
Go to Tools -> SDK Manager. Click on the "SDK Platforms" tab. Make sure the Show Package Details checkbox is pressed.
Be sure to install the Android 8.1 system images and tools are installed by checking the following boxes under "SDK Platforms" tab and hitting "Apply" button:
The code lab will use an Emulator device to run Topeka. To create one, open an Android project and run AVD Manager from Android Studio (Tools -> AVD Manager) and create a new AVD with the following configuration:
Verify the device settings and tap "Finish" to create the emulated device.
Next, we should launch the app in the emulator and start to familiarize ourselves with it:
Click ‘Run' to start the app. Select the emulator from the connected devices and check "Use same selection for future launches"
Verify the app runs and you get the following screen:
When supporting Google Play Instant you can and absolutely should use the same codebase for your instant app and your installable app. Right now Topeka's code is all inside a single app module, which is built using the com.android.application gradle plugin:
By the end of this codelab, you have three modules, built with two new plugins. They are the com.android.instantapp and com.android.feature:
The breakdown of what goes into each module appears below:
In this step, we will convert the existing application module which builds itself into a separate application module which builds a feature module.
Creating a feature module will involve changing the plugin type of app
from com.android.application
to com.android.feature
. We'll also rename the module from app to base (for reasons that we'll explain later).
Then you'll create a installed app module with the name installed
that will use the application plugin. This module will build the APK.
At the end of this step, the project should build an installable APK like it did before, but we'll have separated the build process from the code. This design allows you to easily add the Google Play Instant build process, which we'll do in the next step.
To start, you will switch the app
module type from application to feature.
'app'
to 'base'
Rename the 'app'
to 'base'
:
:base
a Feature moduleNext, we change the module type to Feature module by changing the plugin type from com.android.application
to com.android.feature
in the base/build.gradle
file:
// replace
// apply plugin: 'com.android.application'
// with
apply plugin: 'com.android.feature'
baseFeature true
flagEvery app that supports Google Play Instant must have one feature module marked as the base feature module. You'll learn more about what this means later. For now, since we only have one feature module, we'll make it the base feature module by setting the baseFeature
flag to true
:
android {
...
baseFeature true
...
}
installed
moduleYou've mostly set up your new base feature module which contains all of the app code.
Now you'll create a minimal application module, responsible for building the APK.
From File->New Module...
Select "Phone & Tablet Module", click "Next"
Choose the following:
com.google.samples.apps.topeka.installed
Click "Next".
Select "Add No Activity" and click "Finish".
Android Studio may suggest to add some files to Git repository, click "Cancel". Android Studio may ask you to install missing versions of build tools. Confirm installation to continue.
applicationId
from :base
to :installed
applicationId
will always live within the com.android.application
gradle module. Since base
is no longer an application module, you need to move it from base
's build.gradle
file.
android {
...
defaultConfig {
// remove this line
applicationId "com.google.samples.apps.topeka"
...
}
...
}
And put the applicationId
in the installed
module.
android {
...
defaultConfig {
// replace
// applicationId "com.google.samples.apps.installed"
// with
applicationId "com.google.samples.apps.topeka"
...
}
...
}
Synchronize the gradle files and re-build the project with Build->Rebuild Project. It should build without issue.
:installed
and :base
Now you need to have :installed
build :base
. To do this replace all the compile dependencies in installed/build.gradle
with this single dependency:
dependencies {
implementation project(':base')
}
Remove the application
element from installed/src/main/AndroidManifest.xml
. It should only contain this single manifest
element with the package com.google.samples.apps.topeka
.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.topeka">
</manifest>
:base
Android ManifestMultiple manifests are not allowed to have the same package name. Because of this, we will change the package name in the base AndroidManifest to com.google.samples.apps.topeka.base
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.samples.apps.topeka.base">
...
</manifest>
Because you changed the package, android:name=".activity.QuizActivity"
refers to com.google.samples.apps.topeka.
base.
activity.QuizActivity
but the actual package location is com.google.samples.apps.topeka.activity.QuizActivity
.
You'll now need to fully qualify the path to each of the classes referenced in your :base
AndroidManifest. You can copy the manifest below:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.samples.apps.topeka.base">
<uses-permission android:name="android.permission.INTERNET" />
<application android:allowBackup="false"
android:fullBackupContent="false"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="false"
android:theme="@style/Topeka"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<activity android:name="com.google.samples.apps.topeka.activity.SignInActivity"
android:theme="@style/Topeka.SignInActivity"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.google.samples.apps.topeka.activity.CategorySelectionActivity"
android:theme="@style/Topeka.CategorySelectionActivity" />
<activity android:name="com.google.samples.apps.topeka.activity.QuizActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustPan"
android:theme="@style/Topeka.QuizActivity"/>
</application>
</manifest>
Another side effect of updating the base module's AndroidManifest file is that now various import statements for R will be wrong.
You can fix this by doing a Replace in Path in the app. Edit -> Find -> Replace in Path...
Then replace:
import com.google.samples.apps.topeka.R
with
import com.google.samples.apps.topeka.base.R
Finally sync Gradle files and build this project using Build->Build APKS
If you switch to Project mode, you should see your generated APK in the expected location.
Then Run the app.
The app should behave exactly the same despite all of our changes.
Congratulations! We have just moved the app's core functionality into a shareable feature module and we are now ready to add the instant app module.
Right now we have a feature module called :base
which contains all the code. The :installed
module builds the :base
module as an APK (an installable app). In this step you will create the :instant
module which will build the :base
module as an instant app.
Select File -> New -> New Module...
Choose "Instant App" and click "Next."
Enter Library name as "instant" and click "Finish".
Android Studio adds the new instant app module for your project:
:instant
and :base
Next, we need to update the instant app gradle file to depend on the :base
feature module.
dependencies {
implementation project(':base')
}
Now do a clean rebuild: Build -> Rebuild project.
Users don't install your app when using Google Play Instant, so to open your app, users will usually click on a link. For Google Play Instant, URLs are the entry point into an app and they are associated with Activities. These URLs must be verified App Links.
An App Link is a url which you have proven that you own and that you associate with your Android App. They are not Google Play Instant specific, but Google Play Instant uses them to make their url entry points work.
You'll create an App Link mapping http://topeka.instantappsample.com/signin to the SigninActivity.
To create App Links, you'll use a feature built into Android Studio called "App Links Assistant." Open this tool by going to Tools -> Apps Links Assistant :
From the sidebar, tap on the "Open URL Mapping Editor" button (which appears near the top):
From the editor, tap the "+" button to add a new app link entry:
Create a new URL mapping with the following details:
Host: http://topeka.instantappsample.com
Select "pathPrefix" from dropdown
Path: /signin
Activity: com.google.samples.apps.topeka.activity.SigninActivity (base)
This generates an intent-filter tag in your AndroidManifest. It's nested in the SignInActivity
tag:
<activity
android:name="com.google.samples.apps.topeka.activity.SignInActivity"
android:theme="@style/Topeka.SignInActivity"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="http"
android:host="topeka.instantappsample.com"
android:pathPrefix="/signin" />
</intent-filter>
</activity>
You also want https://topeka.instantappsample.com/signin to map to the SigninActivity
.
From the editor, tap the "+" button to add a new app link entry. Create a new URL mapping with the following details:
Host: https://topeka.instantappsample.com
Select "pathPrefix" from dropdown
Path: /signin
Activity: com.google.samples.apps.topeka.activity.SigninActivity (base)
The SignInActivity
tag should now have both intent filters:
<activity
android:name="com.google.samples.apps.topeka.activity.SignInActivity"
android:theme="@style/Topeka.SignInActivity"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="http"
android:host="topeka.instantappsample.com"
android:pathPrefix="/signin" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="topeka.instantappsample.com"
android:pathPrefix="/signin" />
</intent-filter>
</activity>
When a user clicks on your link, you want them to be directly sent to your instant app, without an annoying disambiguation dialog like the one shown below:
To avoid the disambiguation dialog, add auto verify to your App Link intent filters for http://topeka.instantappsample.com/signin and https://topeka.instantappsample.com/signin. You'll need to open up the Android Manifest for the :base
module and set the attribute android:autoVerify="true"
for the intent filter:
<activity
android:name="com.google.samples.apps.topeka.activity.SignInActivity"
android:theme="@style/Topeka.SignInActivity"
android:windowSoftInputMode="adjustPan">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<...>
</intent-filter>
<intent-filter>
<...>
</intent-filter>
</activity>
There are two other activities in Topeka which you will create App Link url mappings for:
CategorySelectionActivity
QuizActivity
Create these mappings by following steps 3-5 for all of them. In the end you should have the following 6 mappings:
You must define a default URL for your app. It should be on the same activity which opens when your installed app opens. In our case this is the SignInActivity
. You'll add the meta-data
tag to the Activity as shown below:
<activity
android:name="com.google.samples.apps.topeka.activity.SignInActivity"
android:theme="@style/Topeka.SignInActivity"
android:windowSoftInputMode="adjustPan">
<...>
<meta-data android:name="default-url"
android:value="https://topeka.instantappsample.com/signin" />
</activity>
Your final manifest will look like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.samples.apps.topeka.base">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="false"
android:theme="@style/Topeka"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<activity
android:name="com.google.samples.apps.topeka.activity.SignInActivity"
android:theme="@style/Topeka.SignInActivity"
android:windowSoftInputMode="adjustPan">
<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.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="http"
android:host="topeka.instantappsample.com"
android:pathPrefix="/signin" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="topeka.instantappsample.com"
android:pathPrefix="/signin" />
</intent-filter>
<meta-data android:name="default-url"
android:value="https://topeka.instantappsample.com/signin" />
</activity>
<activity
android:name="com.google.samples.apps.topeka.activity.CategorySelectionActivity"
android:theme="@style/Topeka.CategorySelectionActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="http"
android:host="topeka.instantappsample.com"
android:pathPrefix="/categories" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="topeka.instantappsample.com"
android:pathPrefix="/categories" />
</intent-filter>
</activity>
<activity
android:name="com.google.samples.apps.topeka.activity.QuizActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustPan"
android:theme="@style/Topeka.QuizActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="http"
android:host="topeka.instantappsample.com"
android:pathPrefix="/quiz" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="topeka.instantappsample.com"
android:pathPrefix="/quiz" />
</intent-filter>
</activity>
</application>
</manifest>
Sync gradle files if required and rebuild the project.
To run your creation, you'll need to setup a run configuration that says which URL to open.
Click the Run configuration dropdown and choose "Edit Configurations..."
Select instant under Android App.
Replace the text ‘<< ERROR - NO URL SET>>' with https://topeka.instantappsample.com/signin
To run your instant app, select instant from the Run configuration dropdown and click Run:
If you switch to Project mode, you should see the following generated files in Topeka/instant/build/outputs/apk/debug/instant-debug.zip:
To verify the running app is, in fact, an instant app, go to the Recents screen and check:
You should see "Topeka" with the lightning bolt icon.
You have now created and deployed an app with Google Play Instant. You took an existing Android app and restructured it to build both a full installed APK and an Instant APK that will be loaded when the user taps on the associated URLs.
Take a look at the final state of the code here.
In part two of this codelab you'll learn how to refactor the sample app into multiple features, while keeping common code in the base feature.
We would love for you to continue, but if you end this lab now, feel proud that you built your first app with Google Play Instant!
Of course there is the second part of this codelab for creating a multi-feature app with Google Play Instant support. This will allow Topeka to load portions of the code on-demand.
You can check out the second codelab here: Build a Multi-Feature Instant App.
If you want to go deeper on Google Play Instant, here are some links to get you started: