浅析移动端深度链接技术
公司的后台管理App,是使用React Native框架来写的,RN框架屏蔽了IOS和Android之间开发方式的差异,使得前端可以通过JavaScript来开发跨平台的原生App,前段时间收到一个需求,需要在App中打开第三方的App并进入某个特定的界面。于是去看了一下RN的官方文档和网上的一些关于App之间通信的技术文章。了解到了深度链接这个东西,但是很多文章要么讲解的不是很清楚,要么就是很久之前的东西,而且没有针对于RN框架来展开的具体的介绍,所以打算自己写一篇博客给大家分享一下深度链接技术。
什么是深度链接
所谓深度链接,简单来讲,就是你在手机上点击一个链接之后,可以打开某个App或者是直接进入到这个App内部的某个页面,而不是App正常打开时显示的首页。
如何实现深度链接
就目前来讲,实现深度链接主要有以下三种方式,这篇文章我们主要介绍URL Scheme的方式来实现深度链接:
- URL Scheme iOS,Android平台都支持
- Universal Links 只支持iOS9及以上系统
- App Links 只支持Android6.0及以上系统
1.什么是URL Scheme
通过对比网页链接来理解 URL Scheme,应就容易多了。
URL Scheme 有两个单词:
- URL,我们都很清楚,
http://www.apple.com就是个 URL,我们也叫它链接或网址; - Scheme,表示的是一个 URL 中的一个位置—最初始的位置,即
://之前的那段字符。比如http://www.apple.com这个网址的 Scheme 是 http(可以理解为URL地址的协议)。
根据我们上面对 URL Schemes 的了解,我们可以很轻易地理解,在手机中,我们可以像定位一个网页一样,用一种特殊的 URL 来定位一个应用甚至应用里某个具体的功能。而定位这个应用的,就是这个应用的URL 的 Scheme 部分,也就是开头儿那部分。比如短信,就是
sms:下面用苹果的网站和 iOS 上的微信来做个简单对比:
网页(苹果)iOSiOS网站首页/打开应用weixin://子页面/具体功能weixin://dl/moments(朋友圈)在这里,
http://www.apple.com和weixin://都声明了这是谁的地盘。然后在http://www.apple.com后面加上/mac就跳转到从属于http://www.apple.com的一个网页上;同样,在weixin://后面加上dl/moments就进入了微信的一个具体的功能——朋友圈。但是,两者还有几个重要的区别:
- URL,我们都很清楚,
所有网页都一定有网址,不管是首页还是子页。但未必所有的应用都有自己的 URL Scheme,更不是每个应用的每个功能都有相应的 URL Scheme。实际上,现状是,大多数的应用只有用于打开应用的 URL Scheme,而有一些应用甚至没有用于打开应用的 URL Scheme。几乎没有所有功能都有对应 URL 的应用。一个 App 是否支持 URL Schemes 要看那个 App 的作者是否在自己的作品里添加了支持 URL Schemes 相关的代码。
一个网址只对应一个网页,但并非每个 URL Scheme 都只对应一款应用。因为URL Schemes是可以重复的,所以曾经出现过有 App 使用支付宝的 URL Schemes 拦截支付帐号和密码的事件。
2.如何通过URL Scheme实现深度链接
通过下面这张图来说明APP1与APP2之间,在技术上,如何完成横向调用:

假如要从APP-F调用APP-T
1)APP-T要进行自定义scheme的配置(iOS是info文件,Android是activity),并且可以对传入的参数进行处理。
2)APP-F进行调用,首先判断设备是否安装APP-T。
3)如果未安装,则跳转到APP-T的web版应用(假设他提供web版)或者是跳转到AppStore等应用市场进行下载。
4)如果已安装,则调用APP-T配置好的URL SCHEME,直接打开APP-T的相关界面。
自定义URL Scheme配置
要想你的应用支持URL Scheme,你需要在应用里面进行配置,IOS和Android配置的方法是不一样的,下面会详细讲一下两者配置的步骤:
IOS
第一步是创建 URL Scheme — 在 Xcode Project Navigator 中找到并点击工程 info.plist 文件。当该文件显示在右边窗口,在列表上点击鼠标右键,选择 Add Row:
向下滚动弹出的列表并选择 URL types

点击左边剪头打开列表,可以看到 Item 0,一个字典实体。展开 Item 0,可以看到 URL Identifier,一个字符串对象。该字符串是你自定义的 URL scheme 的名字。建议采用反转域名的方法保证该名字的唯一性,比如 com.yourCompany.yourApp。

点击 Item 0 新增一行,从下拉列表中选择 URL Schemes,敲击键盘回车键完成插入。

注意 URL Schemes 是一个数组,允许应用定义多个 URL schemes。

展开该数据并点击 Item 0。你将在这里定义自定义 URL scheme 的名字。只需要名字,不要在后面追加 :// — 比如,如果你输入 iOSDevApp,你的自定义 url 就是 iOSDevApp://

此时,整个定义如下图:

虽然我赞同 Xcode 使用描述性的名字的目的,不过看到创建的实际的 key 也是非常有用的。这里有一个方便的技巧,右键点击 plist 并选择 Show Raw Keys/Values,就能看到以下效果:

还有另一种有用的输出格式,XML,因为可以非常容易的看到字典和原始数组及其包括的实体的结构。点击 plist 并选择 Open As – Source Code:

Android
Android应用/组件间通信有一种方式是
intent,应用可以注册intent filter声明自己对什么样的intent感兴趣,其它应用发送intent时通过系统级广播传递过来,如果与预先注册的intent filter匹配,应用将收到该intent(无论应用是否正在运行,都会被“唤醒”,也就是隐式启动Activity),并取出intent携带的数据,做进一步处理,所以我们需要在AndroidManifest.xml里静态注册intent filter来声明自定义的URL Scheme:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter> <!-- 正常启动 -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter> <!-- URL Scheme启动 -->
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<data android:scheme="myapp"></data>
</intent-filter>
</activity>
React Native提供的Liking API处理深度链接
在RN的官方文档中提供了Liking这个API让我们处理深度链接来实现App之间的跳转。
处理传入的链接
如果你的应用被其注册过的外部url调起,则可以在任何组件内这样获取和处理它:
1 | componentDidMount() { |
注意:getInitialURL方法只有在App第一次启动的时候会执行,如果App已经启动但是进程挂到后台,通过深度链接打开该App的时候不会执行getInitialURL方法,所以需要用到addEventListener方法,确保每次打开App时都能知道我们的App是被哪个链接调起的。
如果要在MainActivity实例存在(即App进程没有关闭的时候)的时候监听传入的intent,那么需要在AndroidManifest.xml中将MainActivity的launchMode设置为singleTask。
1 | <activity |
对于iOS来说,如果要在App启动后也监听传入的App链接,需要在AppDelegate.m中增加以下代码:
1 | // iOS 9.x or newer |
1 | // iOS 8.x or older |
然后你的React组件就可以监听Linking的相关事件:
1 | componentDidMount() { |
打开外部链接
要启动一个链接相对应的应用(打开浏览器、邮箱或者其它的应用),只需调用:
1 | Linking.openURL(url).catch(err => console.error('An error occurred', err)); |
如果想在打开链接前先检查是否安装了对应的应用,则调用以下方法:
1 | Linking.canOpenURL(url).then(supported => { |
注意:在IOS平台,打开外部链接需要配置Scheme白名单
- 在项目的info.plist中添加一LSApplicationQueriesSchemes,类型为Array;
- 添加需要支持的项目,类型为字符串类型;
