Android M 的 App Links 实现详解

发布于 2024-12-26 11:57:09 字数 7770 浏览 3 评论 0

谷歌 2015 年的 I/O 大会上宣布了一个 新特性 :允许开发者将 app 和他们的 web 域名关联。这一举措是为了最小化用户遇到“打开方式”对话框的概率。

比如,我们安装了两个 Twitter 应用 - 官方的和 Falcon Pro 。当你在某个地方点击了 Twitter URL 的时候,你会看到如下的对话框:

Clicking a Twitter link in the Android messaging app app-links-intent-chooser-dialog.png

但是在安卓 M 中,如果一个 app 明确的指定了 App 链接-这个对话框将不复存在。点击一个链接将立即打开官方的 app,没有第三方 app 的机会,更不会打开一个浏览器。

在上图的例子中,当你点击了那个链接,安卓系统会检查是否有一个 app 可以处理 twitter.com URL,然后跟 twitter.com 核对哪个 app(s)可以处理该域名的链接,这样我们就能避免影响用户。

注意安卓并不会在点击链接的时候才核对这些链接,因此在安卓决定使用哪个 app 之前并不会有网络阻塞。关于这点后面有更多讨论。

虽然这会使安卓更方便- 多数情况下,你确实希望点击一个链接打开的是最合适的那个 app- 但是对于那些喜欢使用第三方 app 的人来说,似乎是一件坏事。不过这种行为可以在 Android M 的系统设置中关掉。

app 开发者如何实现 App Links

实现的细节可以在 Android Developers' Preview 网站 上找到。

如果你有一个需要处理链接(比如 example.com)的 app,你应该:

  • 有上传文件到 example.com 根路径的权限,如果没有,你无法让你的 app 成为这些链接的默认打开方式。
  • 在 build.gradle 文件中设置 compileSdkVersion 'android-MNC'。
  • 为每个这样的<intent-filter>标签
<intent-filter>
    <data android:scheme="http" />
    ...
</intent-filter>

添加属性 android:autoVerify="true"。http 也可以是 https。

注:这里对 filter 的写法略去了很多,其实官网(上面的那个链接)有完整的示例:

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

认证是以主机名为单位的而不是以 intent filter 为单位的,因此从技术上讲,并不需要为每个标签都添加该属性,但是添加了也不会出什么问题。

创建 JSON 文件

为了能让安卓可以认证,你的 app 需要被允许使用 app 链接行为,为此,需要提供一个 JSON 文件,JSON 文件中需要包含 app 的 ID 以及 APK 的公钥证书。

这个文件必须包含一个 JSON 数组,数组中可以有一个或者多个对象,每个对象对应一个你想认证的 app ID:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.myapp",
      "sha256_cert_fingerprints": ["6C:EC:C5:0E:34:AE....EB:0C:9B"]
    }
  }
]

比如,你现在有一个 com.example.myapp 的发行版 app,同时还有一个叫 com.example.myapp.beta 的 beta 版 app,你可以通过在数组中设置两个对象来允许两者都可以接受认证,每个对象带有各自的 app ID 和公钥值。

注意,这个文件的验证是非常严格的:数组中的每个对象都必须和上面的那个一模一样。在数组中添加了任意其他的对象,或者对象中有额外的属性都会导致整个验证失败。- 即便这个 app 对象是有效的。

为了防止为同一个构建注册了不同的 key,貌似一个 app 可以指定多个 SHA256 指纹证书,不管怎样,指纹证书可以通过如下方式获得:

echo | keytool -list -v -keystore app.keystore 2> /dev/null | grep SHA256:

上传 JSON 文件

创建完文件之后,你需要上传它同时保证它可以使用这个 URL 访问 http://example.com/.well-known/statements.json。

目前这个 URL 是 http 的,最终的 M 版本将只允许通过 HTTPS 访问该 URL。 在第一个 M 预览版中,重定向到 HTTPS,或者任何其他重定向(301,302 或者 307)貌似都会被忽略并被视为失败。

URL 的 scheme 和<intent-filter>标签中的 android:scheme 值是互不相干的,即使你有一个只接受 HTTPS URLs 的 filter,认证 URL 仍然需要通过 HTTP 访问。

在了解了安卓如何使用这些信息之后,我们来看看如果 debug 这个过程。

Android M 是如何实现 App Links 的

App 链接认证涉及到安卓系统的两个组建:Package Manager 和 Intent Filter Verifier。

PackageManager 是一个无处不在的标准组建 - 它负责验证所安装的 apk 是否有效,授予 app 权限,另外还可以通过它知道系统上安装了些什么 app。

Intent Filter Verifier 则是 Android M 上才有的新玩意儿。这个组建负责获取链接指向的 JSON 认证,解析它,验证它,然后将报告返回给 PackageManger。

虽然这个组建不是用户轻易就能替换的,但似乎系统中只能有一个活动状态的 Intent Filter Verifier - 想要注册成为一个 verifier,必须要有 android.permission.INTENT_FILTER_VERIFICATION_AGENT 权限,而这个权限只有签名了系统密钥的 app 才能得到。

你可以通过如下的命令查看当前激活的 intent filter verifier:

adb shell dumpsys package ifv

在第一个 M 预览版中,com.android.statementservice 完全扮演了这个角色。

How App Links are verified

App 链接认证在安装的时候就一次性完成。这就是为什么刚刚我们说不必在每次点击链接的时候都阻塞网络。

当一个 package 安装的时候,或者现有的 package 升级的时候:

  • 1.PackageManager 对即将安装的 apk 做常规的验证。
  • 2.如果成功,这个 package 将被安装,同时发出一个带有 android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION 的广播 intent,intent 中还携带有该 package 的信息。
  • 3.Intent Filter Verifier 的广播接收器将获取这个广播。
  • 4.从 package 的<intent-filter>标签中编译出一个特有主机名的列表。
  • 5.verifier 尝试从每个特有的主机名中获取 statements.json。
  • 6.每一个被获取的 JSON 文件都会检查它的 application ID 和安装包的证书。
  • 7.只有当所有文件同时满足时,才会发送成功信息到 PackageManager,否则失败。
  • 8.PackageManager 存储结果。

如果认证失败,app 链接将无法指向你的 app - 你的 app 会像往常一样出现在“打开方式”对话框中(除非另一个 app 通过了同一域名的验证)。

就我所了解的而言,认证只会在安装和升级的时候会发生,因此对大多数用户来说,再次通过验证的机会是在 app 下一次升级的时候。

Intent Filter Verifier 的行为

主机名

example.com 和 www.example.com 会被认为是两个独立的主机名。因此要求 statements.json 在两个主机名下都是可直达的。

比如,如果你将所有的请求都重定向到 http://www.example.com/* to https://example.com/*,则会让这个主机名的认证失败,从而导致整个 app 链接认证失败。

这种情况下,你可能需要为你的 web 服务器做特殊的配置,确保每个对于 statements.json 的请求都有能直接返回 HTTP 200。这个约束在后面的版本中可能会有所放松。

响应时间

如果 verifier 不能在 5 秒之内和你的 web 服务器建立链接并接收到 HTTP 响应,认证会失败。

缺少链接环境

同样的,如果在认证开始的时候设备是离线的,或者网络环境很差,认证也会失败。

HTTP 缓存

目前 Intent Filter Verifier 的实现基本遵循 HTTP 缓存规则。

如果你的 statements.json 响应包含了 Cache-Control: max-age=[seconds]头部,那么这个响应将被 verifier 缓存到磁盘。虽然 60 秒以下的'max-age'会被忽略,但是 60 秒似乎也足够了。同样的一个 Expires header 也会被缓存。

如果你有 ETag 或者最近更新的 headers,那么 verifier 将在下一次视情况使用这些值来验证相应的主机。据我所知,如果这些 header 子退出之后没有明确指定缓存控制头,那么响应的缓存时间将是不确定的。

缓存头部只理会 http 200 的响应,如果是一个 404 响应,那么下次将忽略,verifier 需要直接连接主机。

Debugging App Links

当安卓系统试图认证你的 app 链接时,PackageManager 除了向 logcat 中报告 true/false 值之外,还有一些其他反馈,比如:

IntentFilter ActivityIntentInfo{1a61a0a com.example.myapp/.MainActivity}
 verified with result:true and hosts:example.com www.example.com

但是,你可在任意时刻向系统查询 package 的 app 链接认证状态:

adb shell dumpsys package d

这会返回如下的认证条目信息:

Package Name: com.example.myapp
Domains: example.com www.example.com
Status: always

可能会有多个关于你 package 的条目:一个是系统的,零个或者多个用户的- 有些用户的偏好覆盖了系统参数。

可能会产生的状态值大致如下:

  • undefined — app 没有在 manifest 中启用链接自动验证功能。
  • ask — app 验证失败(会通过打开方式对话框询问用户)
  • always — app 通过了验证(点击这个域名总是打开这个 app)
  • never — app 通过了验证,但是系统设置关闭了此功能。

如果你没能通过认证,你可以再次尝试重新安装同一版本:

adb install -r app/build/outputs/apk/app-debug.apk

如果你在安装的时候没有在服务器上看见 statements.json URL 的请求,你可以清空 Intent Verifier 服务的 HTTP 缓存,这样下次就会直接请求服务器:

adb shell pm clear com.android.statementservice

如果你不知道 statements.json 的内容是否正确返回,可以使用安卓模拟器的-tcpdump 选项来检查网络上发送的是什么 - 注意安卓 M 最终版出来之后就没那么容易了,因为数据是加密的。

还有一种选择,那就是使用模拟器的-http-proxy 选项,让所有的网络请求都通过代理,比如 Charles 代理。

总结

虽然在开始我担心 App Links 会取代目前安卓上非常酷的 intent 机制,但是也乐于看到这对于大多数情况都是有用的,况且如果用户不喜欢,使用简单的方法就可以把它关掉。

考虑到 verifier 服务对 JSON 解析和 HTTP 请求异常严格,希望这里所提到的一些细节对你实现 app linking 有所帮助。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
25 人气
更多

推荐作者

情深如许

文章 0 评论 0

小镇女孩

文章 0 评论 0

见鹿。

文章 0 评论 0

atoothache

文章 0 评论 0

github_Ya3wWzbdC

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文