解锁Android的隐藏超链接:Deep Link与App Link探秘

12 阅读20分钟

解锁Android的隐藏超链接:Deep Link与App Link探秘

引言:开启深度链接的奇妙世界

在移动互联网的世界里,你是否常常遇到这样的场景:收到银行的转账短信,点击其中的链接,就能直接打开手机银行 App 并跳转到交易详情页面;又或是在朋友圈看到朋友分享的一篇精彩文章,来自某个资讯 App,点击链接后,神奇地直接唤起了该 App 并定位到对应的文章内容。这些看似平常的操作,背后却隐藏着一项强大的技术 —— 深度链接(Deep Link)。

深度链接,简单来说,就是一种能够让用户从一个应用(如短信、网页、社交媒体等)直接跳转到另一个应用内特定页面的技术,它打破了应用之间的壁垒,为用户提供了更加便捷、流畅的体验,就像是在不同的数字城堡之间搭建了一条条秘密通道,让用户可以快速穿梭其中。

而在 Android 系统中,有两种重要的深度链接实现方式,它们就像深度链接世界里的两员大将,各有所长,这就是 Android Deep Link 和 App Link。接下来,就让我们一起深入探索它们的奥秘,揭开深度链接的神秘面纱 。

Android Deep Link:通用但存瑕的元老

核心原理:城堡的秘密密道

Android Deep Link,也被称为 URL Scheme,是深度链接技术中的 “元老”,自 Android 系统诞生之初便已存在 。如果把每个 App 比作一座独立的城堡,那么 URL Scheme 就像是为每个城堡设计的 “秘密密道”,它有着特殊的暗号格式,比如知乎的密道暗号是zhihu://

其工作机制也不难理解,应用首先要向操作系统 “登记” 自己的密道暗号。当系统遇到类似zhihu://questions/123456这样的暗号时,就会在已安装的应用中寻找所有能 “听懂” 这个暗号的 App,然后弹出一个选择器,询问用户 “你要用哪个城堡的密道?”,也就是让用户选择使用哪个应用来打开这个链接。

短信场景中的应用

短信场景是 Android Deep Link 最常见的应用领域之一。例如,你收到银行发送的转账通知短信,内容可能是这样:


尊敬的客户,您尾号8888的储蓄卡收入5000.00元。
<a href="icbc://transfer/result?order=20240119001">点击查看详情</a>
立即下载:<a href="https://appstore/icbc">应用商店</a>

在这条短信中,<a href="icbc://transfer/result?order=20240119001">点击查看详情</a>就是一个 Deep Link 链接。当用户点击这个链接时,如果手机上已经安装了工商银行的手机银行 App,系统就会尝试打开该 App,并将用户直接带到转账结果详情页面;如果未安装,则会显示无法打开的提示。这种方式为用户提供了极大的便利,无需手动打开 App 再去查找相关交易记录,大大提升了操作效率和用户体验。

实现方式全解析

  • H5 页面触发跳转:在 H5 页面中,有多种方法可以触发 Deep Link 跳转。

    • 直接跳转:使用window.location.href属性直接赋值为 Deep Link 链接,如:

function openAppByScheme() {
    window.location.href = 'zhihu://question/123456';
}
  • 通过 iframe 跳转:这种方式更加稳定,创建一个隐藏的 iframe,将 Deep Link 链接设置为其 src 属性,代码如下:

function openAppByIframe() {
    const iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    iframe.src = 'zhihu://question/123456';
    document.body.appendChild(iframe);
    // 2秒后移除iframe
    setTimeout(() => {
        document.body.removeChild(iframe);
    }, 2000);
}
  • 处理短信链接:在短信中,链接通常是直接可点击的,用户点击<a href="zhihu://question/123">短信链接</a>即可触发跳转,前端无需额外的复杂处理。

  • Android 原生端配置:在 Android 原生开发中,需要在AndroidManifest.xml文件中进行配置,以声明应用对特定 Deep Link 的处理能力。


<!-- 接收Scheme链接的Activity -->
<activity android:name=".DeepLinkActivity"
          android:exported="true">
    <!-- 声明Scheme处理能力 -->
    <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:host="deeplink"
            android:scheme="zhihu" />
        <!-- 可选:更精确的路径匹配 -->
        <data android:pathPrefix="/question/" />
    </intent-filter>
    <!-- 可以声明多个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" />
        <!-- 另一个Scheme -->
        <data android:scheme="zh" />
    </intent-filter>
</activity>

在上述配置中,<intent-filter>标签用于声明该 Activity 能够处理的 Intent 类型。<action android:name="android.intent.action.VIEW" />表示该 Activity 可以处理查看的动作;<category android:name="android.intent.category.DEFAULT" />允许 Activity 被隐式 Intent 启动;<category android:name="android.intent.category.BROWSABLE" />至关重要,它允许从浏览器中安全地启动该 Activity;<data>标签则定义了 URI 模式,包括android:scheme(协议,如zhihu)、android:host(主机名,如deeplink)以及android:pathPrefix(路径前缀,如/question/)等,用于精确匹配不同的链接格式。

  • 在 Activity 中处理传入的链接:当 Activity 接收到 Deep Link 对应的 Intent 时,需要对其进行处理,以实现跳转到相应的页面或执行特定的操作。

public class DeepLinkActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_deeplink);
        // 处理传入的Intent
        handleDeepLink(getIntent());
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 防止重复创建Activity
        setIntent(intent);
        handleDeepLink(intent);
    }

    private void handleDeepLink(Intent intent) {
        Uri uri = intent.getData();
        if (uri != null) {
            String uriString = uri.toString();
            if (uriString.startsWith("zhihu://question/")) {
                String questionId = uri.getLastPathSegment();
                openQuestionDetail(questionId);
            } else if (uriString.startsWith("zhihu://user/")) {
                String userId = uri.getLastPathSegment();
                openUserProfile(userId);
            } else if (uriString.startsWith("icbc://")) {
                handleBankTransaction(uri);
            }
        }
    }

    private void openQuestionDetail(String questionId) {
        // 跳转到问题详情页
        Intent intent = new Intent(this, QuestionDetailActivity.class);
        intent.putExtra("QUESTION_ID", questionId);
        startActivity(intent);
        finish();
    }

    private void openUserProfile(String userId) {
        // 跳转到用户主页
        Intent intent = new Intent(this, UserProfileActivity.class);
        intent.putExtra("USER_ID", userId);
        startActivity(intent);
        finish();
    }

    private void handleBankTransaction(Uri uri) {
        // 处理银行交易相关逻辑
    }
}

在上述代码中,handleDeepLink方法通过判断 Uri 的前缀来确定链接类型,然后根据不同的链接类型解析出相应的参数,并跳转到对应的页面或执行相应的操作。例如,对于知乎的问题链接,解析出问题 ID 并跳转到问题详情页面;对于用户主页链接,解析出用户 ID 并跳转到用户主页。

优缺点大剖析

  • 优点

    • 兼容性极佳:从 Android 1.0 到最新版本,都对 Android Deep Link 提供了良好的支持,这使得它在各种版本的 Android 设备上都能稳定运行,无需担心兼容性问题。

    • 实现简单:开发者只需在AndroidManifest.xml中进行简单的配置,声明应用支持的 URL Scheme,即可实现基本的 Deep Link 功能,无需复杂的服务器配置或额外的技术栈。

    • 短信友好:在短信中使用 Deep Link 非常方便,直接嵌入scheme://path格式的链接,用户点击即可跳转,无需对短信进行特殊处理,也不需要借助其他第三方工具。

  • 缺点

    • 弹出选择器:由于多个应用可以声明相同的自定义 Scheme,当用户点击 Deep Link 链接时,系统会弹出选择器,询问用户使用哪个应用打开链接。这不仅增加了用户的操作步骤,还破坏了用户体验的流畅性,尤其是在用户明确知道想要打开的应用时,这种选择器显得多余。

    • 无法判断是否成功:在 H5 页面触发 Deep Link 跳转时,无法准确判断应用是否成功打开。虽然可以使用定时器方案来进行大致判断,但这种方法并不准确,存在一定的误差,无法满足对跳转结果精确判断的需求。

    • 容易被劫持:恶意应用可以注册与合法应用相同的 Scheme,从而劫持 Deep Link 链接。当用户点击链接时,可能会被引导至恶意应用,导致信息泄露、安全风险增加等问题,给用户和合法应用开发者带来损失。

    • 微信 / QQ 中被屏蔽:在微信、QQ 等社交应用中,为了保障用户安全和应用生态的健康,会拦截非白名单的 Scheme 链接。这使得 Android Deep Link 在这些主流社交平台上无法正常使用,限制了其传播和推广的范围。

    • 无降级方案:如果用户手机上未安装对应的应用,点击 Deep Link 链接后会直接显示无法打开的提示,没有提供合理的降级方案,比如跳转到应用商店进行下载,这会导致用户体验的中断,降低用户对应用的好感度。

综上所述,Android Deep Link 虽然具有兼容性好、实现简单等优点,但也存在诸多缺点,在实际应用中需要根据具体场景和需求谨慎使用,并结合其他技术来弥补其不足 。

Android App Link:官方出品的优雅方案

核心原理:基于域名的所有权验证

如果说 Android Deep Link 是民间的 “小道密道”,那么 Android App Link 则是官方打造的 “高速公路”,更加规范、安全、高效 。App Link 是 Android 6.0(API 级别 23)引入的一种深度链接技术,它的出现主要是为了解决 Android Deep Link 存在的一些问题,如弹出选择器、安全性低等 。

App Link 基于 HTTP/HTTPS 协议,其核心原理是通过验证应用对某个域名的所有权,来实现无歧义的深度链接。简单来说,就是应用需要向系统证明自己是某个域名的合法拥有者,这样当用户点击指向该域名的链接时,系统就会直接打开对应的应用,而不会弹出选择器询问用户 。

为了便于理解,我们可以将这个过程想象成一个快递配送系统。每个 App 就像是一个收件人,而链接中的域名则是收件地址。在普通的配送方式(类似 Android Deep Link)中,当快递员(系统)拿到一个包裹(链接)时,由于有多个收件人(应用)可能声称对这个地址有接收权,快递员就会询问周围的人(弹出选择器):“这个包裹该给谁?” 而在 App Link 的 “官方快递系统” 中,收件人(应用)提前向快递站(系统)提供了自己的指纹信息(通过域名所有权验证),当快递员拿到包裹时,只需要核对指纹(验证域名所有权),如果匹配,就可以直接将包裹送到收件人手中,无需再询问其他人 。

具体的验证流程如下:

  1. 在 AndroidManifest.xml 中声明:应用首先需要在AndroidManifest.xml文件中声明支持的 HTTP/HTTPS Intent Filter,例如:

<activity android:name=".AppLinkActivity">
    <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="example.com"
            android:pathPrefix="/article/" />
    </intent-filter>
</activity>

在上述代码中,android:autoVerify="true"表示应用请求系统在安装时自动验证其对指定域名的所有权;<data>标签定义了应用能够处理的链接格式,包括android:scheme(协议,必须是 http 或 https)、android:host(主机名,即域名)以及android:pathPrefix(路径前缀,用于更精确地匹配链接) 。 2. 在网站上发布验证文件:应用开发者需要在对应的网站上发布一个 Digital Asset Links JSON 文件(通常位于/.well-known/assetlinks.json),该文件包含了应用的包名和签名证书的 SHA-256 指纹等信息,用于证明应用对该域名的所有权 。例如:


[{
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
        "namespace": "android_app",
        "package_name": "com.example.app",
        "sha256_cert_fingerprints": ["ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890"]
    }
}]
  1. 系统验证:当用户安装应用时,系统会自动下载并验证这个 JSON 文件。如果验证成功,系统就会将该应用与对应的域名关联起来。此后,当用户点击指向该域名的链接时,系统就会直接打开对应的应用,并将链接传递给应用进行处理 。

实现步骤详解

  • 配置 AndroidManifest.xml:在AndroidManifest.xml中,为需要处理 App Link 的 Activity 添加intent-filter,示例如下:

<activity android:name=".AppLinkActivity">
    <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="example.com"
            android:pathPrefix="/article/" />
        <!-- 可以添加多个data标签,以支持不同的链接格式 -->
        <data
            android:scheme="https"
            android:host="example.com"
            android:pathPrefix="/user/" />
    </intent-filter>
</activity>
  • 配置服务器端:在服务器上创建并放置assetlinks.json文件,文件内容格式如下:

[{
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
        "namespace": "android_app",
        "package_name": "com.example.app",
        "sha256_cert_fingerprints": ["YOUR_SHA256_CERT_FINGERPRINT"]
    }
}]

其中,YOUR_SHA256_CERT_FINGERPRINT需要替换为应用签名证书的 SHA-256 指纹。获取 SHA-256 指纹的方法如下:

  • 使用命令行工具:如果是使用 Gradle 构建的项目,可以在项目根目录下的命令行中执行以下命令(假设使用的是 JDK 1.8 及以上版本):

./gradlew signingReport

在输出结果中,找到对应构建类型(如releasedebug)的SHA-256指纹。

  • 使用 Android Studio:在 Android Studio 中,依次点击Build -> Generate Signed Bundle / APK,按照向导完成操作。在最后一步,点击View Certificate,即可查看证书的详细信息,包括 SHA-256 指纹 。

优势尽显

  • 无歧义直接打开:由于通过域名所有权验证,系统能够明确知道应该使用哪个应用来打开链接,避免了弹出选择器的困扰,为用户提供了更加流畅、无缝的体验,就像快递直接送达收件人手中,无需中间的询问环节 。

  • 无缝体验:用户从外部链接直接进入应用内的特定页面,整个过程自然流畅,无需额外的操作,大大提升了用户体验,增强了用户对应用的好感度 。

  • 安全性高:基于 HTTP/HTTPS 协议和域名所有权验证,有效防止了链接被劫持,保障了用户的信息安全和应用的正常使用,就像给快递加上了一把安全锁 。

  • SEO 友好:使用标准的 HTTP/HTTPS 链接,有利于搜索引擎对应用内容的索引和收录,提高应用的曝光率和可发现性 。

  • 未安装处理:如果用户未安装应用,点击 App Link 链接时,系统会自然降级到对应的网页,为用户提供了合理的备用方案,避免了用户体验的中断 。

当然,App Link 也并非完美无缺,它也存在一些不足之处:

  • 配置相对复杂:相比 Android Deep Link,App Link 的配置过程涉及到服务器端的配置和域名所有权验证,对于开发者来说,需要更多的技术知识和操作步骤 。

  • 版本限制:App Link 仅支持 Android 6.0(API 级别 23)及以上版本的系统,这意味着在一些老旧设备上无法使用,限制了其应用范围 。

  • 国内特殊环境:在国内,由于网络环境和应用生态的特殊性,可能会存在一些兼容性问题或无法正常验证域名所有权的情况,需要开发者进行额外的适配和调试 。

总体而言,Android App Link 以其独特的优势,为深度链接带来了更加优质的解决方案,尤其适用于追求极致用户体验和高安全性的应用场景 。

两者深度对比:差异与选择

在深入了解了 Android Deep Link 和 App Link 之后,我们来对它们进行一番全面的对比,以便在实际开发中能够根据项目的具体需求做出明智的选择 。

对比维度Android Deep LinkAndroid App Link
适用版本全系列版本,从 Android 1.0 起就已存在,兼容性极佳,能够覆盖所有 Android 设备,无论是老旧机型还是最新款手机,都能稳定支持 Deep Link 功能仅支持 Android 6.0(API 级别 23)及以上版本,在一些运行 Android 6.0 以下系统的设备上无法使用,这在一定程度上限制了其应用范围 ,但随着 Android 系统的不断更新换代,高版本系统的设备占比逐渐增加,App Link 的应用场景也在不断扩大
适用协议支持自定义协议,开发者可以根据项目需求自由定义 URL Scheme,如myapp:// ,这种灵活性使得 Deep Link 在一些特定场景下能够更好地满足业务需求,例如在企业内部应用中,可以使用自定义协议来实现特定功能的深度链接仅支持 HTTP/HTTPS 协议,基于标准的网络协议,这使得 App Link 在安全性和通用性方面具有优势,同时也有利于与 Web 端进行无缝集成,方便进行 SEO 优化和跨平台交互
安全验证不进行验证,这使得 Deep Link 存在一定的安全风险,恶意应用可以注册相同的自定义 Scheme,从而劫持链接,导致用户被引导至恶意应用,造成信息泄露、安全威胁等问题需要进行客户端与服务器端的双向验证,通过在服务器上放置 Digital Asset Links JSON 文件,验证应用对域名的所有权,确保链接只能被指定的应用处理,有效防止了链接被劫持,保障了用户的信息安全和应用的正常使用 ,就像给链接加上了一把坚固的安全锁
用户体验通常会弹出选择器,当多个应用声明相同的自定义 Scheme 时,系统会询问用户使用哪个应用打开链接,这增加了用户的操作步骤,破坏了用户体验的流畅性,尤其是在用户明确知道想要打开的应用时,这种选择器显得多余默认直接启动目标 App,用户点击链接后能够直接进入应用内的特定页面,无需额外的操作,提供了更加流畅、无缝的用户体验,就像乘坐直达电梯一样,快速到达目的地
实现难度实现相对简单,只需在AndroidManifest.xml中进行简单的配置,声明应用支持的 URL Scheme 即可,无需复杂的服务器配置或额外的技术栈,对于开发者来说,上手难度较低配置相对复杂,除了在AndroidManifest.xml中进行配置外,还需要在服务器端创建并放置assetlinks.json文件,进行域名所有权验证,这需要开发者具备一定的服务器端开发知识和操作经验 ,但一旦配置完成,能够为应用带来更优质的深度链接体验
未安装处理无合理的降级方案,如果用户手机上未安装对应的应用,点击 Deep Link 链接后会直接显示无法打开的提示,导致用户体验的中断,无法引导用户进行进一步的操作,如下载应用有合理的降级方案,如果用户未安装应用,点击 App Link 链接时,系统会自然降级到对应的网页,用户可以在网页上进行相关操作,或者引导用户下载应用,为用户提供了更加友好的体验,避免了用户体验的中断 ,就像为用户准备了一条备用通道
通过以上对比可以看出,Android Deep Link 和 App Link 各有优劣。在实际项目中,如果需要兼容所有 Android 版本,或者对链接的灵活性有较高要求,且对安全性和用户体验的要求相对较低,可以选择使用 Android Deep Link;如果项目主要面向 Android 6.0 及以上版本的设备,追求极致的用户体验和高安全性,并且愿意投入更多的时间和精力进行配置,那么 Android App Link 无疑是更好的选择 。在一些情况下,也可以同时使用两者,利用 Deep Link 的兼容性和 App Link 的优势,为用户提供更加全面、优质的深度链接服务 。

实践建议与避坑指南

在实际项目中使用 Android Deep Link 和 App Link 时,以下是一些实用的建议和需要注意的问题 。

同步配置两者

为了确保在不同的 Android 版本和场景下都能提供良好的深度链接体验,建议同时配置 Android Deep Link 和 App Link 。以电商 App 为例,在进行营销活动时,通过短信向用户发送活动链接。如果只配置了 App Link,那么在 Android 6.0 以下版本的设备上,链接将无法正常工作,导致用户无法参与活动,从而影响活动效果和用户体验 。而如果同时配置了 Deep Link,即使在低版本设备上,也能通过 Deep Link 实现基本的跳转功能,保证了活动的覆盖范围和用户参与度 。

点击链接无反应

  • 原因:最常见的原因是没有匹配到 Intent Filter,可能是 scheme、host、path 等配置与实际链接不一致 。例如,在配置 Intent Filter 时,将 scheme 声明为myapp,而实际链接中使用的是myApp(注意大小写),这样就会导致无法匹配,点击链接无反应 。

  • 解决方案:仔细检查AndroidManifest.xml中 Intent Filter 的配置,确保与链接中的 scheme、host、path 等完全一致,包括大小写 。可以使用 ADB 命令进行测试,如adb shell am start -a android.intent.action.VIEW -d "myapp://detail?id=123",如果能成功打开应用,则说明配置正确;如果提示Activity not started等错误信息,则需要进一步检查配置 。

链接被浏览器拦截

  • 原因:未在 Intent Filter 中声明BROWSABLE类别,浏览器无法识别该链接可以被应用处理,从而将其拦截 。

  • 解决方案:在AndroidManifest.xml中为处理链接的 Activity 添加<category android:name="android.intent.category.BROWSABLE" />,确保浏览器能够将链接正确地传递给应用 。例如:


<activity android:name=".DeepLinkActivity"
          android:exported="true">
    <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:host="deeplink"
            android:scheme="myapp" />
    </intent-filter>
</activity>

App Links 未验证成功

  • 原因:通常是assetlinks.json文件配置错误,如文件路径不正确、内容格式有误、SHA-256 指纹与应用签名证书不匹配等 。例如,在生成assetlinks.json文件时,使用了错误的应用签名证书来获取 SHA-256 指纹,导致验证失败 。

  • 解决方案:使用adb shell am verify-app-links命令检查验证状态,根据提示信息进行排查 。确保assetlinks.json文件放置在正确的服务器路径下,如https://example.com/.well-known/assetlinks.json;仔细检查文件内容,确保package_namesha256_cert_fingerprints与应用实际情况一致 。如果指纹不匹配,需要重新获取正确的 SHA-256 指纹并更新assetlinks.json文件 。获取 SHA-256 指纹的方法可以参考前文提到的使用命令行工具或 Android Studio 的方式 。

多个应用注册相同 scheme 冲突

  • 原因:当多个应用声明了相同的自定义 Scheme 时,系统无法确定应该使用哪个应用来打开链接,从而导致冲突 。例如,两个不同的新闻类 App 都注册了news://的 Scheme,用户点击news://article/123链接时,系统会弹出选择器询问用户使用哪个应用打开,这不仅影响用户体验,还可能导致用户误操作 。

  • 解决方案:优先使用 App Link(HTTPS),因为 App Link 通过域名所有权验证,可以避免多个应用注册相同 scheme 的冲突问题 。如果必须使用自定义 Scheme,尽量使用独特的、不易与其他应用冲突的 Scheme,并且在应用中做好提示和引导,告知用户可能出现的选择器情况,并提供清晰的操作指引 。