深度链接的探索和Flutter中的应用

3,176 阅读11分钟

1. 背景

你是否曾经有过这样的经历?你的好友分享给你一个B站的链接,你想要看1080P(高码率)的清晰度,这需要打开B站APP才能满足你的需求。在生活中,其实我们会遇到很多很多类似的场景,需要在一个APP中打开另外一个APP,并且进入到指定页面。

随着人们生活水平的提高,APP已经成为大众生活不可或缺的一部分。用户量是一个APP产品的重要指标,是APP的生命之源。在成熟的社交、工作软件中能够打开APP,则是一个很好的引流方式,也极大地提高用户体验。 在我们的手机系统中,每一个APP都是独立的存在。它们相互独立,信息隔离,犹如海面上的一座座孤岛。那么,如何将他们关联起来呢?Deep Link(深度链接)就登上了历史舞台。

如果把 App 看成网站,那么Deep Link就是网站中的深入页面,比如B站某个视频页面,知乎某个问题页面,京东某个商品页面等等。简单理解,就是当用户点击手机中的某个链接时,可以帮助用户打开APP并跳转到目标页面,直接实现场景还原。极大程度地提升用户体验。 DeepLink 对于App社交分享、App广告引流、App裂变活动、Web to App、沉默用户唤醒等场景,对广告引流、活动推广、新闻类、电商类、游戏类、视频直播类App的引流推广和转化都有着不错的效果。

2. 什么是深度链接?

在移动环境中,深度链接指将用户从任何地方引导到特定APP内部特定页面的操作。它能直接跳转到目标App具体位置的技术,深度链接打破了网站与App间的壁垒,成为实现网站与App相互跳转的桥梁。

3. 深度链接的工作原理

若用户已安装应用,他们会无缝链接到特定的页面。 若用户并未安装应用程序,可以通过延迟深度链接,将用户引导到下载页面,用户安装并启动之后,跳转到指定内容页面。

4. 唤醒APP的方式

唤醒APP在最早基本是使用URL Scheme的方式,但是这种方式最大的问题是不够安全且用户体验不好。2015年,Google和Aple分别发布新的技术来支持深度链接,Android阵营是App Link,iOS阵营是Universal Link。

技术Universal LinksApp LinksURI Schemes
平台要求>= iOS 9>= Android 6-
未安装表现打开Web页面打开Web页面报错
跳转下载页面不能
链接格式https URLhttps URL自定义协议URL

APP Link

概述

APPLinks需要你的APP注册一个域名,在用户安装/更新期间,Android系统将验证App Links配置是否符合服务器端assetlinks.json文件。如果Android系统监听到你点击了可以识别的域名,且该域名在已安装APP中注册过,则会拉起该APP。

优势

  • 安全且具体:Android 应用链接使用链接到应用所属的网站网域的 HTTP 网址,因此其他应用都无法使用相应链接唤起APP,这是与URI Scheme较大的不同。Android 应用链接的要求之一,就是用户要通过Android的某个网站关联方法验证您对网域的所有权。
  • 顺畅的用户体验:由于 Android 应用链接针对您的网站和应用中的相同内容使用单个 HTTP 网址,因此未安装应用的用户会直接转到APP所属域名下的网站,不会显示 404,也不会出现错误。
  • 通过 Google 搜索吸引用户:用户可以通过在移动浏览器、Google 搜索应用、Android 中的屏幕搜索中或通过 Google 助理点击来自 Google 的网址,直接打开应用中的特定内容。

配置流程

  • 在AndroidManifast.xml新增一个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" />
     <!-- Accepts URIs that begin with "https://www.example.com/gizmos” -->
     <data android:scheme="https"
         android:host="www.hansDemo.com"
         android:path="/test" />
 </intent-filter>

以上配置告诉安卓去验证一个文件,这个文件地址是www.hansDemo.com/.well-known… 如果存在这个文件,同时验证成功,那么用户点击该域名之下的链接时,就可以直接拉起APP,实现无缝跳转。

  • 配置assetlinks.json文件 需要确认package_name和打包证书的SHA256指纹。SHA256指纹可通过运行命令获取keytool -list -v -keystore [打包证书路径]

    [{
       "relation": ["delegate_permission/common.handle_all_urls"],
       "target": {
         "namespace": "android_app",
         "package_name": "com.example.test",
         "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文件也可以在Android Studio配置生成。详情可参见文档

  • 将assetlinks.json文件放置于服务器项目下的.well-known文件夹中。 这一步需要联系后台开发的小伙伴。若在浏览器访问(https://域名/.well-known/assetlinks.json), 可以正常返回assetlinks.json文件,则配置成功。

Universal Link

概述

iOS APP可以配置Associated Domains,当APP安装或更新的过程中,系统会从制定域名的.well-known/apple-app-site-association路径访问Universial Links配置。若验证通过,则证明该域名与APP有对应关系。当用户点击特定链接是,iOS系统会检查当前域名是否在APP有注册,有则拉起APP。

优点

  • Custom URL scheme是自定义的协议,因此在没有安装该app的情况下是无法直接打开的。而Universal Links本身也就是一个能够指向一个web页面或者app中的内容页的标准的web link(形如example.com) 因此能够很好的兼容其他情况。也就是说,当已经安装了这个app的时候,不需要加载任何web页面,app就会立即启动;当这个app没有安装的时候,就会默认地从当前浏览器中重定向到App Store中引导用户去下载安装这个app。
  • Universal links是从服务器上查询是哪个app需要被打开,因此不存在Custom URL scheme那样名字被抢占、冲突的情况。
  • Universal links支持从其他app中的UIWebView中跳转到目标app
  • 安全性,用universl link去打开的时候,只有你可以通过创建和上传一个允许这个网页去通过这个URL去打开你的app的文件。
  • 隐私性,提供Universal link给别的app进行app间的交流,然而对方并不能够用这个方法去检测你的app是否被安装。

配置和运行

  • 配置 App ID 支持 Associated Domains 登录developer.apple.com/ 苹果开发者中心,找到对应的 App ID,在 Application Services 列表里将Associated Domains设置为Enabled即可。
  • 配置 iOS App 工程 Xcode 11.0以上 版本:工程配置中相应功能:targets->Signing&Capabilites->Capability->Associated Domains,在其中的 Domains 中填入你想支持的域名。注意必须以 **applinks:**为前缀,且只填写域名,path部分不需要填写。

image.png

  • 配置和上传 apple-app-association 域名必须支持Https。域名根目录或者.well-known目录下放置文件apple-app-association不带任何后缀。这一关键步骤需要后台的同事配合!! 文件格式如下:
// appID:组成方式是teamID.bundle_identifier。如下面的 9JA89QERDS 就是 
// teamId。在Apple开发者中心,在 Account -> Membership 里面可以找到 Team ID。
// paths:设定APP支持的路径列表,只有这些指定的路径的链接,才能被APP所处理。
{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "9JA89QERDW.com.hans.test",
                "paths": [ "/test"]
            }
        ]
    }
}

我们可以通过search.developer.apple.com/appsearch-v… 来验证Universal Links配置是否成功。

URL Scheme

概述

URI Scheme是最为原始的唤醒APP方式,只需在Native注册scheme,用户点击到此类链接时,就会自动唤醒APP。而URL的路由机制,还可以跳转到指定页面,实现深度链接。

缺点

  • 风格不一的提示框

我们只能通过固定协议格式的链接来实现跳转,而且打开H5页面时,会出现一个提示框:“是否打开XXX”。用户确认了才会跳转到App中,增加了用户操作。而且不同系统和浏览器弹出框风格不一,难以管理。

  • 未安装APP导致的异常

错误处理情况因平台不同,难以统一处理,部分APP会直接跳错误页(比如Android Chrome/41,iOS中老版的Lofter);也有的停留在原页面,但弹出提示“无法打开网页”(比如iOS7);iOS8以及最新的Android Chrome/43 目前都是直接停留在当前页,不会跳出错误提示。

  • 场景支持情况

iOS在实际使用中,腾讯系的微信,QQ明确禁止使用,iOS9以后Safari不再支持通过js,iframe等来触发scheme跳转,并且还加入了确认机制,使得通过js超时机制来自动唤醒APP的方式基本不可用;Android平台则各个app厂商差异很大,比如Chrome从25及以后就同Safari情况一样。

  • 命名冲突或劫持

如果手机中同時存在有两个应用都使用相同的 URL Scheme,那么唤起目标应用时,系统会优先唤起哪一个呢?Apple在后续iOS版本(iOS 11)采用了先到先得原则,如果使用了同一个URL Scheme,只有先安装的app会被启动。然而,攻击者还是可以通过其他方法来利用这个漏洞。这也是URI Scheme比较大的问题,所以我们推荐Universal Link和App Link。

  • 兼容性问题

微信、QQ等禁用URL Scheme,但是它们都各自维护着一个白名单,如果Scheme不在该白名单内,那么就不能在App内打开这个目标App。

4. 模拟用户点击行为

如果你的scheme定义为hansDemo,网站域名为www.hansDemo.com:

Android

adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "hansDemo://www.hansDemo.com"'

iOS

/usr/bin/xcrun simctl openurl booted "hansDemo://www.hansDemo.com"

如果你是使用Universal Links和APP Links来实现Deep Links的话,那么scheme为https

image.png

5. 在Flutter中的应用

在上文我们已经了解了Universal Links和APP Links的基本情况和基本的配置方法。而Deep Link的一大优势是能够拉起APP的同时,重现指定场景,跳转到相应页面。如果你的配置没有问题,那么当用户点击链接则可以顺利拉起APP,那么APP中的跳转应该怎么实现呢?

这里我们以Flutter为例子进行讲解。Flutter这两年迅速成长,已经成为炙手可热的移动端跨平台技术方案。如果我们要在Flutter应用中实现Deep Link,首先是要完成对Native的配置,在上一章节我们已经讲解。而APP内部的跳转,则推荐使用uni_links插件。

截屏2022-06-15 下午6.41.07.png 在插件文档中,我们可以发现,最关键步骤是通过uriLinkStream.listen()来实现对拉起APP行为的监听。我们获取到唤醒APP的URL后,可以根据实际项目需求,实现相关逻辑。示例代码如下:

Future<void> initUniLinks(BuildContext context) async {
  try {
    /// 当APP是因为链接被唤醒的情况
    final String? initialLink = await getInitialLink();
    if (initialLink != null) {
      final Uri initialLinkUri = Uri.parse(initialLink);
      /// TODO: 在获取到本次拉起APP的URI之后,你需要它做什么
    }

    /// 当APP运行中,有链接拉起了APP
    uriLinkStream.listen((Uri? uri) {
      if (uri != null) {
        /// TODO: 在获取到本次拉起APP的URI之后,你需要它做什么
      }
    });
  } on PlatformException {
    /// 处理PlatformException
  } on FormatException {
    /// 处理FormatException
  }
}

如果你希望唤醒APP能够有不同的行为,可以将相关参数携带在URL的query中,比如: https://test.hansDemo.com/test?userId=hans&userType=admin

提取query中的内容可以按如下代码处理:

uriLinkStream.listen((Uri? uri) async {
  /// 获取queryParamters,得到Map
  final Map<String, dynamic>? queryMap = uri?.queryParameters;
  
}, onError: (dynamic error) {
  
});

6. 结语

深度链接实现从任何地方将用户带到应用内容页。这项技术已经给移动互联网带来极大便利跟价值。现如今,深度链接将会被广泛地应用,成为移动生活中,连接APP的重要纽带。

  • 提升了用户的体验。比如我在微信打开B站链接,如果我要对某个视频进行评论,只能在BiliBili APP中进行。如果没有深度链接,那我们只能在桌面端打开B站,重新搜索相应视频,再进行评论;而有了深度链接,只需在微信端点击"打开APP”,就可以直接跳转到APP中对应视频的页面进行评论。
  • 提高数据的转化。不管是短信营销、邮件营销还是社会化营销,对于用户来说不再是一个无效的链接,而是可以直达其目的页,这样,就可以将本来属于App的流量又回到App里,进而促成交易的达成。
  • 对于整个移动互联网来说,实现App间的连通,推进整个社会前进的效率。通过这个技术,我们可以联想到很多场景,比如在大众点评上搜了一个饭店后,通过深度链接就可以无缝进行打车,而不是退出大众点评再从手机桌面启动高德地图进行打车。这也大大提高生活效率,让人民生活更便利。