{"id":1252,"date":"2026-05-29T17:00:00","date_gmt":"2026-05-29T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1252"},"modified":"2026-03-07T03:49:01","modified_gmt":"2026-03-07T08:49:01","slug":"android-instant-apps-deep-links","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/android-instant-apps-deep-links\/","title":{"rendered":"Android Instant Apps and Deep Linking"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Android Instant Apps (now called Google Play Instant) let users run parts of your app without installing it. A user taps a link, and the relevant module loads instantly, no Play Store visit, no download progress bar, no &quot;Open&quot; button. They&#39;re using your app within seconds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The entire Instant Apps experience is built on deep linking. The URL determines which module to load, what content to show, and how to handle the user&#39;s session. Getting deep linking right is the difference between a seamless instant experience and a blank screen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers how Instant Apps interact with App Links, how to configure deep linking for instant modules, and how to convert instant users into full installs. For the App Links foundation, see the <a href=\"https:\/\/tolinku.com\/blog\/android-app-links-complete-guide\/\">Android App Links complete guide<\/a>. For understanding fallback behavior when the app isn&#39;t installed, see the <a href=\"https:\/\/tolinku.com\/blog\/deep-link-fallback-behavior\/\">deep link fallback behavior guide<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Instant Apps Work<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">When a user taps a URL that&#39;s associated with an Instant App:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Android checks<\/strong> if the URL maps to an installed app (standard App Links). If yes, the installed app opens.<\/li>\n<li><strong>If not installed<\/strong>, Android checks if the URL maps to an Instant App module on Google Play.<\/li>\n<li><strong>If an instant module exists<\/strong>, Android downloads the module (typically 4-10 MB) and runs it immediately.<\/li>\n<li><strong>If no instant module exists<\/strong>, the URL opens in the browser as normal.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The key insight: Instant Apps use the same URL structure and verification mechanism as regular App Links. The only difference is what happens when the app isn&#39;t installed.<\/p>\n\n\n\n<pre><code>User taps URL\n    \u251c\u2500 App installed? \u2192 Open app (standard App Links)\n    \u2514\u2500 Not installed?\n        \u251c\u2500 Instant module available? \u2192 Load instant module\n        \u2514\u2500 No instant module? \u2192 Open in browser\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before implementing Instant App deep links:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><p><strong>Your app must be modularized.<\/strong> Instant Apps require <a href=\"https:\/\/developer.android.com\/guide\/app-bundle\" rel=\"nofollow noopener\" target=\"_blank\">Android App Bundles<\/a> and feature modules. Each instant-enabled feature is a separate module.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>App Links must be verified.<\/strong> Instant Apps use the same <a href=\"https:\/\/developers.google.com\/digital-asset-links\/v1\/getting-started\" rel=\"nofollow noopener\" target=\"_blank\">Digital Asset Links<\/a> verification as regular App Links. Your <code>.well-known\/assetlinks.json<\/code> must be valid.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Target URL must be declared.<\/strong> Each instant module declares which URLs it handles in the manifest.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Module must be under the size limit.<\/strong> Individual instant feature modules must be under 15 MB (total instant app under 15 MB).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configuring Deep Links for Instant Modules<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Module Structure<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A typical Instant App has:<\/p>\n\n\n\n<pre><code>app\/\n\u251c\u2500\u2500 base\/          (shared code, always loaded)\n\u251c\u2500\u2500 feature-main\/  (main installable feature)\n\u251c\u2500\u2500 feature-product\/  (instant-enabled: product detail)\n\u2514\u2500\u2500 feature-checkout\/ (instant-enabled: checkout flow)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Manifest Configuration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Each instant module declares its URL handling in its own <code>AndroidManifest.xml<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-xml\">&lt;!-- feature-product\/src\/main\/AndroidManifest.xml --&gt;\n&lt;manifest xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    xmlns:dist=&quot;http:\/\/schemas.android.com\/apk\/distribution&quot;\n    package=&quot;com.example.app.feature.product&quot;&gt;\n\n    &lt;dist:module\n        dist:instant=&quot;true&quot;\n        dist:title=&quot;Product Detail&quot;&gt;\n        &lt;dist:fusing dist:include=&quot;true&quot; \/&gt;\n    &lt;\/dist:module&gt;\n\n    &lt;application&gt;\n        &lt;activity\n            android:name=&quot;.ProductDetailActivity&quot;\n            android:exported=&quot;true&quot;&gt;\n            &lt;intent-filter android:autoVerify=&quot;true&quot;\n                           android:order=&quot;1&quot;&gt;\n                &lt;action android:name=&quot;android.intent.action.VIEW&quot; \/&gt;\n                &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; \/&gt;\n                &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; \/&gt;\n                &lt;data\n                    android:scheme=&quot;https&quot;\n                    android:host=&quot;yourapp.com&quot;\n                    android:pathPattern=&quot;\/products\/.*&quot; \/&gt;\n            &lt;\/intent-filter&gt;\n\n            &lt;meta-data\n                android:name=&quot;default-url&quot;\n                android:value=&quot;https:\/\/yourapp.com\/products&quot; \/&gt;\n        &lt;\/activity&gt;\n    &lt;\/application&gt;\n&lt;\/manifest&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Key elements:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>dist:instant=&quot;true&quot;<\/code>: Marks this module as instant-enabled.<\/li>\n<li><code>android:autoVerify=&quot;true&quot;<\/code>: Required for App Links verification.<\/li>\n<li><code>android:order=&quot;1&quot;<\/code>: Priority when multiple filters match. Higher numbers are checked first.<\/li>\n<li><code>default-url<\/code>: The fallback URL if the module can&#39;t handle the specific URL.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Build Configuration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In your app-level <code>build.gradle.kts<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">android {\n    \/\/ Enable Instant Apps\n    dynamicFeatures += setOf(\n        &quot;:feature-product&quot;,\n        &quot;:feature-checkout&quot;\n    )\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In each feature module&#39;s <code>build.gradle.kts<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">plugins {\n    id(&quot;com.android.dynamic-feature&quot;)\n}\n\nandroid {\n    \/\/ Instant module configuration\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Handling Deep Link Data in Instant Modules<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Extracting URL Parameters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When the instant module launches, extract the deep link data from the intent:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">class ProductDetailActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        val uri = intent.data\n        if (uri == null) {\n            \/\/ No deep link data; show default product list\n            showProductList()\n            return\n        }\n\n        \/\/ Extract product ID from the URL path\n        \/\/ URL: https:\/\/yourapp.com\/products\/abc123\n        val productId = uri.lastPathSegment\n\n        \/\/ Extract optional query parameters\n        \/\/ URL: https:\/\/yourapp.com\/products\/abc123?ref=campaign1\n        val referralSource = uri.getQueryParameter(&quot;ref&quot;)\n\n        if (productId != null) {\n            loadProduct(productId, referralSource)\n        } else {\n            showProductList()\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Sharing State Between Instant and Installed App<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When a user installs the full app after using the instant version, you want to preserve their session. Use the <a href=\"https:\/\/developers.google.com\/android\/reference\/com\/google\/android\/gms\/instantapps\/PackageManagerCompat\" rel=\"nofollow noopener\" target=\"_blank\">Cookie API<\/a> to transfer data:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">import com.google.android.gms.instantapps.InstantApps\n\n\/\/ In the instant module: save state before install\nfun saveInstantState() {\n    val cookie = JSONObject().apply {\n        put(&quot;user_session&quot;, &quot;session_token_here&quot;)\n        put(&quot;cart_items&quot;, cartItemsJson)\n        put(&quot;referral_source&quot;, referralSource)\n    }\n\n    InstantApps.getPackageManagerCompat(this)\n        .setInstantAppCookie(cookie.toString().toByteArray())\n}\n\n\/\/ In the installed app: retrieve state after install\nfun restoreFromInstant() {\n    val cookieBytes = InstantApps.getPackageManagerCompat(this)\n        .getInstantAppCookie()\n\n    if (cookieBytes.isNotEmpty()) {\n        val cookie = JSONObject(String(cookieBytes))\n        val session = cookie.getString(&quot;user_session&quot;)\n        val cartItems = cookie.getString(&quot;cart_items&quot;)\n        \/\/ Restore the user&#39;s session and cart\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The cookie is limited to 16 KB, so store only essential state (session tokens, referral attribution, cart contents).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Converting Instant Users to Full Installs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The goal of an Instant App is to demonstrate value quickly and motivate the full install. Deep links play a role in this conversion.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Show an Install Prompt at the Right Moment<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Don&#39;t show the install prompt immediately. Let the user experience value first:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ Show install prompt after the user completes a meaningful action\nfun onProductAddedToCart() {\n    if (InstantApps.isInstantApp(this)) {\n        \/\/ The user found a product they want; now prompt for install\n        showInstallPrompt()\n    }\n}\n\nfun showInstallPrompt() {\n    InstantApps.showInstallPrompt(\n        this,\n        intent,        \/\/ The current intent (preserves deep link data)\n        REQUEST_INSTALL,\n        &quot;referral_source&quot; \/\/ Optional referrer string for Play Store attribution\n    )\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Preserve Deep Link Context Through Install<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When the user installs from an instant module, the install intent can carry the original deep link data:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">fun showInstallPrompt() {\n    val postInstallIntent = Intent(Intent.ACTION_VIEW).apply {\n        data = Uri.parse(&quot;https:\/\/yourapp.com\/products\/${currentProductId}&quot;)\n        setPackage(packageName)\n        addCategory(Intent.CATEGORY_BROWSABLE)\n    }\n\n    InstantApps.showInstallPrompt(\n        this,\n        postInstallIntent,\n        REQUEST_INSTALL,\n        null\n    )\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After installation, the full app opens with the same deep link, and the user lands on the same product they were viewing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Deep Link Routing: Instant vs. Installed<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Your deep link routing logic needs to handle both contexts:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">class DeepLinkRouter : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        val uri = intent.data ?: return finish()\n        val isInstant = InstantApps.isInstantApp(this)\n\n        when {\n            uri.path?.startsWith(&quot;\/products&quot;) == true -&gt; {\n                if (isInstant) {\n                    \/\/ In instant mode: show product detail (read-only)\n                    navigateToProduct(uri.lastPathSegment, readOnly = true)\n                } else {\n                    \/\/ In installed mode: show full product experience\n                    navigateToProduct(uri.lastPathSegment, readOnly = false)\n                }\n            }\n            uri.path?.startsWith(&quot;\/checkout&quot;) == true -&gt; {\n                if (isInstant) {\n                    \/\/ Checkout requires full install\n                    showInstallPrompt()\n                } else {\n                    navigateToCheckout()\n                }\n            }\n            else -&gt; navigateToHome()\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Testing Instant App Deep Links<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Using ADB<\/h3>\n\n\n\n<pre><code class=\"language-bash\"># Launch an instant module via deep link\nadb shell am start -a android.intent.action.VIEW \\\n  -d &quot;https:\/\/yourapp.com\/products\/abc123&quot; \\\n  --es &quot;callerId&quot; &quot;com.google.android.instantapps.supervisor&quot; \\\n  --es &quot;instantAppPackageName&quot; &quot;com.example.app&quot;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Using Android Studio<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a run configuration for the instant module.<\/li>\n<li>Set the launch URL in the configuration.<\/li>\n<li>Run on a device with Google Play services.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Common Test Scenarios<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Direct URL tap<\/strong>: Tap the URL in a browser. Should load the instant module.<\/li>\n<li><strong>App already installed<\/strong>: Tap the same URL. Should open the installed app, not the instant module.<\/li>\n<li><strong>Install from instant<\/strong>: Use the instant module, then install. Session state should transfer.<\/li>\n<li><strong>Invalid URL<\/strong>: Tap a URL that doesn&#39;t match any module. Should fall back to browser.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Instant Apps and Tolinku Deep Links<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/deep-linking\">Tolinku deep links<\/a> work with Instant Apps through the standard App Links mechanism. When a user taps a Tolinku link:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>If the app is installed, Tolinku&#39;s redirect opens the app via App Links.<\/li>\n<li>If the app is not installed, the redirect falls through to the web fallback or app store.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">To support Instant Apps with Tolinku links, ensure your Tolinku domain&#39;s redirect chain leads to a URL that your instant module handles. Configure the web fallback in your <a href=\"https:\/\/tolinku.com\/docs\/developer\/app-links\/\">route settings<\/a> to point to the URL associated with the instant module.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Limitations and Considerations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Size constraints.<\/strong> Each instant feature module must be under 15 MB. This limits what you can include. Heavy features (video editing, AR, complex maps) may not fit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>API restrictions.<\/strong> Instant Apps can&#39;t access certain APIs: background services, device identifiers, notification posting. Check the <a href=\"https:\/\/developer.android.com\/topic\/google-play-instant\/getting-started\/instant-enabled-app-bundle\" rel=\"nofollow noopener\" target=\"_blank\">restricted API list<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Geographic availability.<\/strong> Google Play Instant is available in most countries but may not work on all devices (requires Google Play services 11.8+).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Analytics attribution.<\/strong> Instant App sessions are separate from installed app sessions. Make sure your analytics tool can link instant sessions to subsequent installs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the full App Links setup, see the <a href=\"https:\/\/tolinku.com\/blog\/android-app-links-complete-guide\/\">Android App Links guide<\/a>. For understanding how links are verified, see <a href=\"https:\/\/tolinku.com\/blog\/verified-vs-unverified-app-links\/\">verified vs. unverified App Links<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Integrate deep linking with Android Instant Apps. Allow users to try your app instantly via deep links without a full install, and convert them to full installs.<\/p>\n","protected":false},"author":2,"featured_media":1251,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Android Instant Apps and Deep Linking: Complete Guide","rank_math_description":"Integrate deep linking with Android Instant Apps. Allow users to try your app instantly via deep links without a full install.","rank_math_focus_keyword":"android instant apps deep links","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-android-instant-apps-deep-links.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-android-instant-apps-deep-links.png","footnotes":""},"categories":[10],"tags":[25,23,20,111,67,69,26],"class_list":["post-1252","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","tag-android","tag-app-links","tag-deep-linking","tag-google-play","tag-instant-apps","tag-mobile-development","tag-user-acquisition"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1252","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=1252"}],"version-history":[{"count":3,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1252\/revisions"}],"predecessor-version":[{"id":2570,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1252\/revisions\/2570"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1251"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1252"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1252"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1252"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}