Android DeepLink跳转

5,951 阅读7分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

之前无意间听别人提到DeepLink这个名词一脸懵逼,完全没听说过啊,很汗颜。因为一直从事Android TV开发,很少接触类似的概念,所以补充下下相关知识吧。

不同于WEB端通过Http链接就可以跳转到任意界面,出于各种各样的原因在移动端,不同App间和App与浏览器间是不能自由跳转的。这使得移动端APP成为一个个孤岛般的存在,为了将APP链接起来,使之可以在不同APP和APP与浏览器之间自由跳转,故退出了DeepLink相关技术。

Android原生跳转

在了解DeepLink之前我们先来看一下Android原生的页面跳转实现:

  • 显式跳转
  • 隐式跳转

显式跳转

  • 声明

    <activity
         android:name=".main.MainActivity"
         android:exported="true">
         <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           		 <category android:name="android.intent.category.DEFAULT"/>
         </intent-filter>
    </activity>
    
  • 跳转

    val intent = Intent(this,MainActivity::class.java)
    intent.putExtra("param", "传值")
    startActivity(intent)
    

这种跳转是Android中最基础也最常用的页面跳转方式,通过指定目标Activity class对象或者指定完成的包名和类路径,实现页面跳转。这种方式显然不适合用在App间的自由跳转。

隐式跳转

  • 声明

    <activity
         android:name=".main.MainActivity"
         android:exported="true">
         <intent-filter>
           	   <!-- 声明自定义Action -->
               <action android:name="com.zhong.action.TEST"/>
           		 <category android:name="android.intent.category.DEFAULT"/>
         </intent-filter>
    </activity>
    
  • 跳转

    val intent = Intent()
    intent.action = "com.zhong.action.TEST"
    intent.putExtra("param", "传值")
    startActivity(intent)
    

这种跳转方式不需要直接对目标Activity进行引用或者指定完整的类路径,只需要指定自定义的意图,系统就可以完成页面间的跳转。但是App中通常有多个页面,多个页面的Action的定义与维护就会成为问题,而且不同App之间的Action定义千差万别,依然不能很好的实现APP间的自由跳转。

DeepLink

深层链接,是一种使用Uri链接实现跳转到APP任意界面的技术,可以实现在网页和app间自由跳转。

为了更好的理解,我们在看几个常见的适用场景:

  • 场景一

    当我们在淘宝/京东等电商app中进行商品分享,通常会在app复制商品链接,然后通过微信/短信等通讯工具,分享给对方。如何实现对方打开链接之后直接跳转到电商app而不是打开网页或者进入电商app手动搜索呢?

  • 场景二

    我们发动分享链接之后,如果对方没有安装对应的app,我们希望可以跳转到应用市场进行下载,并且在下载完成之后启动app直接跳转到分享商品的界面,有如何实现呢?

  • 场景三

    我们通过浏览器搜索商品后,点击商品可以自动打开电商app并展示商品页面,或者在商品网页上显示“用app打开页面”,点击后跳转app并展示商品页面,又如何实现呢?

上述三种都是DeepLink方案的适用场景。

适用DeepLink可以带来以下好处:(直接引用Google官方的说明,并不很准确了,基本意思理解到就可以了)

  • 安全且具体:Android 应用链接使用链接到您自己的网站网域的 HTTP 网址,因此其他应用都无法使用您的链接。Android 应用链接的要求之一,就是您要通过我们的某个网站关联方法验证您对网域的所有权。
  • 顺畅的用户体验:由于 Android 应用链接针对您的网站和应用中的相同内容使用单个 HTTP 网址,因此未安装应用的用户会直接转到您的网站(而不是应用),不会显示 404,也不会出现错误。
  • Android 免安装应用支持:借助 Android 免安装应用,用户无需安装即可运行您的 Android 应用。如需向您的 Android 应用添加免安装应用支持,请设置 Android 应用链接并访问 g.co/InstantApps
  • 通过 Google 搜索吸引用户:用户可以通过在移动浏览器、Google 搜索应用、Android 中的屏幕搜索中或通过 Google 助理点击来自 Google 的网址,直接打开应用中的特定内容。

DeepLink实现

<activity
    android:name=".main.SecondActivity"
    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:scheme="zhong"
				android:host="www.zhong.com"        
				android:pathPrefix="/second" />
		</intent-filter>
</activity>
  1. Action

    为了响应系统跳转意图,需要将Action指定为android.intent.action.VIEW

  2. category

    添加android.intent.category.DEFAULT来响应系统隐式跳转请求。

    添加android.intent.category.BROWSABLE是为了响应浏览器跳转请求,如果需要从浏览器中启动app,或者支持点击http链接跳转app,需要添加此category。

  3. data

    • android:scheme——URI 的架构部分。

      • 必须指定,要不然后面的属性无效。
      • 指定的架构应不带尾随冒号(例如,应指定 http,而不是 http:),通常使用小写。
      • 支持自定义或者使用http/https,如果想实现在浏览器中点击url跳转,需要指定http/https。
    • android:host——URI 授权方的主机部分.

      • 必须指定,要不然后面的属性无效。
      • 支持第一个属性使用(*)做通配符,如:*.google.com
      • 支持自定义,如果scheme是http/https需要指定有意义的域名。
    • android:port URI 授权方的端口部分。

      • 可省略
      • 指定端口,不解释
    • android:path/pathPrefix/pathPattern

      • 都是指定匹配路的。

      • 必须以/开头。

      • path

        • 匹配完整路径
      • pathPrefix

        • 只与 Intent 对象中的路径的初始部分匹配的部分路径
      • pathPattern

        • 匹配完成路径,但支持通配符(*)
        • 星号(“*”)匹配出现零次到多次的紧邻前面的字符的一个序列。
        • 句点后跟星号(“.*”)匹配零个到多个字符的任意序列。
      • 好像有点绕,举个例子:

        intent跳转路径http://www.zhong.com/second/test,三种类型模式需要匹配需要写成:

        • path —— /second/test

        • pathPrefix —— /second

        • pathPattern —— /second/*

有时候我们可能需要指定多组intent过滤器,比如想同时支持android:scheme使用自定义和http,需要定义多组<intent-filter>,而不能在一组<intent-filter>定义多个<data>

    <activity
        android:name="com.example.android.GizmosActivity"
        android:label="@string/title_gizmos" >
        <intent-filter android:label="@string/filter_view_http_gizmos">
            <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 "http://www.example.com/gizmos” -->
            <data android:scheme="http"
                  android:host="www.example.com"
                  android:pathPrefix="/gizmos" />
            <!-- note that the leading "/" is required for pathPrefix-->
        </intent-filter>
        <intent-filter android:label="@string/filter_view_example_gizmos">
            <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 "example://gizmos” -->
            <data android:scheme="example"
                  android:host="gizmos" />
        </intent-filter>
    </activity>
    

直接应用官方解释吧:

请注意,<data> 元素是这两个 intent 过滤器的唯一区别。虽然同一过滤器可以包含多个 <data> 元素,但如果您想要声明唯一网址(例如特定的 schemehost 组合),则创建单独的过滤器很重要,因为同一 intent 过滤器中的多个 <data> 元素实际上会合并在一起以涵盖合并后属性的所有变体。例如,请参考以下示例:

    <intent-filter>
      ...
      <data android:scheme="https" android:host="www.example.com" />
      <data android:scheme="app" android:host="open.my.app" />
    </intent-filter>
    

看起来这似乎仅支持 https://www.example.comapp://open.my.app。但是,实际上除了这两种之外,它还支持 app://www.example.comhttps://open.my.app

参数接收与跳转

  • 参数接收,通过intent接收参数

    val appLinkAction = intent.action
    val appLinkData: Uri? = intent.data
    if(appLinkAction == Intent.ACTION_VIEW){
        val lastPathSegment = appLinkData?.lastPathSegment
        val host = appLinkData?.host
        val scheme = appLinkData?.scheme
        val params = appLinkData?.pathSegments
    
        Log.d(TAG, "parseUri: lastPathSegment " + lastPathSegment
        + " host = " + host + " scheme = " + scheme)
    }
    
  • 跳转

    val intent = Intent()
    intent.setCalss()
    intent.data = Uri.parse("http://wwww...../aa")
    startActivity(intent)
    

    adb跳转:

    adb shell am start -a android.intent.action.VIEW -d "http://www...../aa"
    

AppLink

通过上面的方法我们定义了如下的DeepLink:

<activity
    android:name=".main.SecondActivity"
    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:scheme="http"
				android:host="www.zhong.com"        
				android:pathPrefix="/second" />
		</intent-filter>
</activity>

当在浏览器或者短信中点击链接http://www.zhong.com/second系统会弹出应用选择对话框(也称为消除歧义对话框),来让用户选择处理此内容的app是哪个。这样使得用户操作流程不顺畅,为了避免这个问题,Google在Android 6.0之后提供AppLink功能(只支持Android 6.0+设备)。

AppLink会在应用安装时,去指定scheme和hots的地址查询对应的配置文件,如果匹配成功,会把此应用作为此URL的默认打开应用,也就是说之后打开上面链接不会在弹出应用选择对话框,而是直接用app打开URL,当然用户可以在设置中手动修改默认打开应用。

如果应用安装时,验证失败、设备未联网或者设备是Android 6.0以下,AppLink会自动退化为DeepLink,而不影响使用。

AppLink相比DeepLink添加了更多约束条件:

  • android:scheme必须是http或者https

  • android:host必须是有效的服务器域名

  • 添加android:autoVerify="true"属性

     <intent-filter android:autoVerify="true" >
       <!-- ... -->
    </intent-filter>
    

AppLink的相关配置方法参考官方链接:添加 Android 应用链接

参考链接: