验证 Android 应用链接

Android 应用链接是一种特殊类型的深层链接,可让您的网站网址直接在您的 Android 应用中打开相应内容(无需用户选择应用)。

要向应用添加 Android 应用链接,请定义使用 HTTP 网址打开应用内容的 intent 过滤器(如创建指向应用内容的深层链接中所述),并验证您是否为相关应用和网站网址的所有者(如本指南中所述)。如果系统成功验证您是网址所有者,则会自动将这些网址 intent 路由到您的应用。

要验证您对应用和网站的所有权,您需要执行以下步骤:

  • 在清单中请求自动验证应用链接。这样即可向 Android 系统说明其应该验证您的应用是否属于 intent 过滤器中使用的网址网域。
  • 通过在以下位置托管 Digital Asset Links JSON 文件,声明您的网站和 intent 过滤器之间的关系:
    https://domain.name/.well-known/assetlinks.json

您可以在以下资源中找到相关信息:

  • 在 Android Studio 中向应用添加 Android 应用链接和 Firebase App Indexing
  • 创建语句列表
  • 深层链接和应用链接之间的区别

    深层链接是一种 intent 过滤器,可让用户直接进入 Android 应用中的特定 Activity。点击此类链接可能会打开一个消除歧义对话框,该对话框可以让用户从多个能够处理给定网址的应用(包括您的应用)中选择一个。例如,图 1 显示的是在用户点击地图链接后打开的消除歧义对话框,该对话框会询问用户是在 Google 地图中还是 Chrome 中打开此链接。

    图 1. 消除歧义对话框

    Android 应用链接是一种深层链接,它们基于已验证属于您网站的网站网址。因此,点击某个此类链接会立即打开您的应用(如已安装),并且不会显示消除歧义对话框。不过,用户以后可以更改处理这些链接的偏好设置。

    下表介绍了更多具体区别。

    深层链接应用链接
    intent 网址协议 httphttps 或自定义协议 需要 httphttps
    intent 操作 任何操作 需要 android.intent.action.VIEW
    intent 类别 任何类别 需要 android.intent.category.BROWSABLEandroid.intent.category.DEFAULT
    链接验证 需要通过 HTTPS 协议在您的网站上发布 Digital Asset Links 文件
    用户体验 可能会显示一个消除歧义对话框,以供用户选择用于打开链接的应用 无对话框;您的应用会打开以处理您的网站链接
    兼容性 所有 Android 版本 Android 6.0 及更高版本

    请求应用链接验证

    要为您的应用启用链接处理验证,请在应用清单内的网址 intent 过滤器中设置 android:autoVerify="true",所选的过滤器可以是具有 android.intent.action.VIEW intent 操作并属于 android.intent.category.BROWSABLE intent 类别的任意过滤器,如以下清单代码段所示:

        <activity ...>
    
            <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="www.example.com" />
                <data android:scheme="https" />
            </intent-filter>
    
        </activity>
        

    如果您的任一 intent 过滤器中存在 android:autoVerify="true",则在搭载 Android 6.0 及更高版本的设备上安装您的应用会导致系统尝试验证与应用的任何 intent 过滤器中的网址相关联的所有主机。验证涉及以下方面:

    1. 系统会检查所有包含以下各项的 intent 过滤器:
      • 操作:android.intent.action.VIEW
      • 类别:android.intent.category.BROWSABLEandroid.intent.category.DEFAULT
      • 网络协议:httphttps
    2. 对于在上述 intent 过滤器中找到的每个唯一主机名,Android 会在相应网站上查询位于 https://hostname/.well-known/assetlinks.json 的 Digital Asset Links 文件。

    仅当系统为清单中的所有主机找到匹配的 Digital Asset Links 文件后,才会将您的应用确立为处理指定网址模式的默认处理程序。

    支持多个主机的应用链接

    系统必须能够对照每个相应网域上托管的 Digital Asset Links 文件验证应用网址 intent 过滤器的 <data> 元素中指定的每个主机。如果任何验证失败了,应用便无法通过验证,因此不能成为应用 intent 过滤器中定义的任何网址模式的默认处理程序。然后,系统会默认采用标准行为来解析相应 intent,具体如创建指向应用内容的深层链接中所述。

    例如,如果在 https://www.example.com/.well-known/assetlinks.jsonhttps://www.example.net/.well-known/assetlinks.json 未找到 assetlinks.json 文件,则具有以下 intent 过滤器的应用无法通过验证:

        <application>
    
          <activity android:name=”MainActivity”>
            <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="www.example.com" />
              <data android:scheme="https" />
            </intent-filter>
          </activity>
          <activity android:name=”SecondActivity”>
            <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="www.example.net" />
            </intent-filter>
          </activity>
    
        </application>
        

    请注意,同一 intent 过滤器中的所有 <data> 元素会合并在一起以涵盖合并后属性的所有变体。例如,上面的第一个 intent 过滤器包含一个仅声明 HTTPS 协议的 <data> 元素。但是,该元素与其他 <data> 元素组合在一起,所以此 intent 过滤器支持 http://www.example.comhttps://www.example.com。因此,如果您想要定义 URI 协议和网域的特定组合,则必须创建单独的 intent 过滤器。

    支持多个子网域的应用链接

    Digital Asset Links 协议将 intent 过滤器中的子网域视为唯一的独立主机。因此,如果您的 intent 过滤器列出多个包含不同子网域的主机,则必须在每个网域上分别发布一个有效的 assetlinks.json。例如,以下 intent 过滤器将 www.example.commobile.example.com 视为接受的 intent 网址主机。因此,必须在 https://www.example.com/.well-known/assetlinks.jsonhttps://mobile.example.com/.well-known/assetlinks.json 分别发布一个有效的 assetlinks.json

        <application>
          <activity android:name=”MainActivity”>
            <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="www.example.com" />
              <data android:scheme="https" android:host="mobile.example.com" />
            </intent-filter>
          </activity>
        </application>
        

    或者,如果您使用通配符声明主机名(例如 *.example.com),则必须在根主机名 (example.com) 发布 assetlinks.json 文件。例如,只要将 assetlinks.json 文件发布到 https://example.com/.well- known/assetlinks.json,具有以下 intent 过滤器的应用便会通过针对 example.com 的任何子域名(例如 foo.example.com)的验证:

        <application>
          <activity android:name=”MainActivity”>
            <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" />
            </intent-filter>
          </activity>
        </application>
        

    声明网站关联性

    必须在您的网站上发布 Digital Asset Links JSON 文件,以指示与网站相关联的 Android 应用并验证应用的网址 intent。JSON 文件使用下列字段标识关联的应用:

    • package_name:在应用的 build.gradle 文件中声明的应用 ID
    • sha256_cert_fingerprints:应用签名证书的 SHA256 指纹。您可以利用 Java 密钥工具,通过以下命令生成该指纹:
          $ keytool -list -v -keystore my-release-key.keystore
          
      此字段支持多个指纹,这些指纹可用于支持不同版本的应用,例如调试编译版本和正式编译版本。

    以下示例 assetlinks.json 文件可授予 com.example Android 应用打开链接的权限:

        [{
          "relation": ["delegate_permission/common.handle_all_urls"],
          "target": {
            "namespace": "android_app",
            "package_name": "com.example",
            "sha256_cert_fingerprints":
            ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
          }
        }]
        

    将网站与多个应用相关联

    网站可在同一 assetlinks.json 文件中声明与多个应用的关联性。以下文件列表展示的是一个语句文件示例,该文件分别声明了与两个应用的关联性,并且位于 https://www.example.com/.well-known/assetlinks.json

        [{
          "relation": ["delegate_permission/common.handle_all_urls"],
          "target": {
            "namespace": "android_app",
            "package_name": "com.example.puppies.app",
            "sha256_cert_fingerprints":
            ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
          }
          },
          {
          "relation": ["delegate_permission/common.handle_all_urls"],
          "target": {
            "namespace": "android_app",
            "package_name": "com.example.monkeys.app",
            "sha256_cert_fingerprints":
            ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
          }
        }]
        

    不同的应用可以处理同一网络主机下的不同资源对应的链接。例如,app1 可以针对 https://example.com/articles 声明一个 intent 过滤器,而 app2 可以针对 https://example.com/videos 声明一个 intent 过滤器。

    注意:与一个网域相关联的多个应用可以使用同一证书或不同证书进行签名。

    将多个网站与同一应用相关联

    多个网站可在各自的 assetlinks.json 文件中声明与同一应用的关联性。以下文件列表示例展示了如何声明 example.com 和 example.net 与 app1 的关联性。第一个列表展示了 example.com 与 app1 的关联性:

    https://www.example.com/.well-known/assetlinks.json

        [{
          "relation": ["delegate_permission/common.handle_all_urls"],
          "target": {
            "namespace": "android_app",
            "package_name": "com.mycompany.app1",
            "sha256_cert_fingerprints":
            ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
          }
        }]
        

    下一个列表展示了 example.net 与 app1 的关联性。唯一的区别是托管这些文件的位置(.com.net):

    https://www.example.net/.well-known/assetlinks.json

        [{
          "relation": ["delegate_permission/common.handle_all_urls"],
          "target": {
            "namespace": "android_app",
            "package_name": "com.mycompany.app1",
            "sha256_cert_fingerprints":
            ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
          }
        }]
        

    发布 JSON 验证文件

    您必须在以下位置发布 JSON 验证文件:

    https://domain.name/.well-known/assetlinks.json

    请确保以下几点:

    • 使用内容类型 application/json 发布 assetlinks.json 文件。
    • 无论应用的 intent 过滤器是否将数据协议声明为 HTTPS,都必须可通过 HTTPS 连接访问 assetlinks.json 文件。
    • assetlinks.json 文件必须可直接访问,没有任何重定向(无 301 或 302 重定向),并且漫游器可访问(您的 robots.txt 必须允许抓取 /.well-known/assetlinks.json)。
    • 如果您的应用链接支持多个主机网域,则必须在每个网域上分别发布 assetlinks.json 文件。请参阅支持多个主机的应用链接
    • 请勿发布清单文件中的开发/测试网址无法供公众访问的应用(例如,任何只可通过 VPN 访问的应用)。作为这种情况下的应急措施,您可以配置编译变体以针对开发编译版本生成不同的清单文件。

    测试应用链接

    在实现应用链接功能时,您应该测试链接功能,以确保系统能够将您的应用和网站相关联,并按预期方式处理网址请求。

    要测试现有语句文件,您可以使用语句列表生成器和测试器工具。

    确认需要验证的主机列表

    测试时,您应该对系统需要针对您的应用验证的已关联主机列表进行确认。创建一个列表,在其中列出对应的 intent 过滤器包含以下属性和元素的所有网址:

    • android:scheme 属性,其值为 httphttps
    • 具有网域网址模式的 android:host 属性
    • android.intent.action.VIEW 类别元素
    • android.intent.category.BROWSABLE 类别元素

    使用此列表检查每个指定的主机和子网域上是否提供了 Digital Asset Links JSON 文件。

    确认 Digital Asset Links 文件

    对于每个网站,使用 Digital Asset Links API 确认是否已正确托管和定义 Digital Asset Links JSON 文件:

        https://digitalassetlinks.googleapis.com/v1/statements:list?
           source.web.site=https://domain.name:optional_port&
           relation=delegate_permission/common.handle_all_urls
        

    测试网址 intent

    确认要与您的应用关联的网站列表,并且确认托管的 JSON 文件有效后,请立即在您的设备上安装应用。等待至少 20 秒,让系统完成异步验证流程。使用以下命令检查系统是否验证了您的应用并设置了正确的链接处理政策:

        adb shell am start -a android.intent.action.VIEW \
            -c android.intent.category.BROWSABLE \
            -d "http://domain.name:optional_port"
        

    您可以在测试过程中检查系统当前的链接处理设置。使用以下命令获取已连接设备上所有应用的现有链接处理政策列表:

        adb shell dumpsys package domain-preferred-apps
        

    执行以下命令也可以实现此操作:

        adb shell dumpsys package d
        

    注意:请确保在安装应用后等待至少 20 秒,以让系统完成验证流程。

    以上命令会返回设备上定义的每个用户或个人资料的清单,前面带有以下格式的标头:

        App linkages for user 0:
        

    在此标头后面,输出会使用以下格式列出相应用户的链接处理设置:

        Package: com.android.vending
        Domains: play.google.com market.android.com
        Status: always : 200000002
        

    此列表可以指示对于此用户,哪些应用与哪些网域相关联:

    • Package - 通过软件包名称标识应用(清单中声明的名称)。
    • Domains - 显示其网络链接由此应用处理的主机的完整列表(使用空格作为分隔符)。
    • Status - 显示此应用的当前链接处理设置。已通过验证且清单中包含 android:autoVerify="true" 的应用会显示 always 状态。此状态后的十六进制数字与 Android 系统的用户应用链接偏好设置记录有关。此值并不表示验证是否成功。

    注意:如果用户在验证完成前更改应用的应用链接设置,则即使验证失败,验证可能会误报为已成功。不过,如果用户显式允许该应用在不进行询问的情况下打开支持的链接,则即使出现这种验证失败的情况也没关系。这是因为用户偏好设置优先于程序化验证(或缺少验证)。因此,链接会直接转到您的应用,而不会显示对话框,就好像验证已成功一样。

    测试示例

    要让应用链接验证成功,系统必须能够针对您在应用的 intent 过滤器中指定,并且符合应用链接标准的所有网站验证您的应用。以下示例展示了定义多个应用链接的清单配置:

        <application>
    
            <activity android:name=”MainActivity”>
                <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="www.example.com" />
                    <data android:scheme="https" android:host="mobile.example.com" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.BROWSABLE" />
                    <data android:scheme="https" android:host="www.example2.com" />
                </intent-filter>
            </activity>
    
            <activity android:name=”SecondActivity”>
                <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="account.example.com" />
                </intent-filter>
            </activity>
    
              <activity android:name=”ThirdActivity”>
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:scheme="https" android:host="map.example.com" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.BROWSABLE" />
                    <data android:scheme="market" android:host="example.com" />
                </intent-filter>
              </activity>
    
        </application>
        

    对于上述清单,平台会尝试验证以下主机:

        www.example.com
        mobile.example.com
        www.example2.com
        account.example.com
        

    对于上述清单,平台不会尝试验证以下主机:

        map.example.com (it does not have android.intent.category.BROWSABLE)
        market://example.com (it does not have either an “http” or “https” scheme)
        

    如需详细了解语句列表,请参阅创建语句列表