Overview

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.

Update Android Studio

Ensure you are using Android Studio 3.0. If you are using an older version of Android Studio, download version 3.0 or later.

Fetch Sample Project

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:

Download Zip

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:

Install Instant App SDK

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"

Install Android API 27 SDK and Tools

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:

Create an Emulated Device

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:

Running Topeka

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.

1. Rename 'app' to 'base'

Rename the 'app' to 'base':

2. Make :base a Feature module

Next, 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:

base/build.gradle

// replace
// apply plugin: 'com.android.application'
// with
apply plugin: 'com.android.feature'

3. Add baseFeature true flag

Every 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:

base/build.gradle

android {
    ...
    baseFeature true
    ...
}

4. Create the :installed module

You'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:

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.

5. Move 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.

base/build.gradle

android {
   ...
   defaultConfig {
       // remove this line
       applicationId "com.google.samples.apps.topeka"
       ...

   }
   ...
}

And put the applicationId in the installed module.

installed/build.gradle

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.

6. Associate :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:

installed/build.gradle

dependencies {
    implementation project(':base')
}

7. Create a minimal AndroidManifest for installed

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.

installed/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.google.samples.apps.topeka">
</manifest>

8. Update the package in :base Android Manifest

Multiple 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

base/src/main/AndroidManifest.xml

<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>

9. Fully qualify Activity class name

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:

base/src/main/AndroidManifest.xml

<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>

10. Update imports of R

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

11. Run installed module and make sure it builds

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.

1. Create an instant app module

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:

2. Associate :instant and :base

Next, we need to update the instant app gradle file to depend on the :base feature module.

instant/build.gradle

dependencies {
    implementation project(':base')
}

Now do a clean rebuild: Build -> Rebuild project.

3. Add your first App Link with App Links Assistant

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:

base/manifests/AndroidManifest.xml

<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>

4. Add https variant

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:

base/manifests/AndroidManifest.xml

<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>

5. Add android:autoVerify="true"

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:

base/manifests/AndroidManifest.xml

<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>

6. Repeat for /category and /quiz links

There are two other activities in Topeka which you will create App Link url mappings for:

Create these mappings by following steps 3-5 for all of them. In the end you should have the following 6 mappings:

7. Add default url

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:

base/manifests/AndroidManifest.xml

<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:

base/manifests/AndroidManifest.xml

<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.

8. Setup your Run Configuration

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

9. Run your instant app

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.

Congratulations!

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:

Main Docs

Code Samples

UX Guidelines

FAQ