02 RN App 外部唤醒踩坑记

公司新企划的 RN 项目需要实现链接分享功能,用户直接通过分享链接唤醒 App 并跳转相应页面,同时该功能要求在 iOS 和 Android 双端兼容,在此记录下拥抱新技术的踩坑历程。

Universal Links

在 iOS 中,唤醒功能是通过 Universal Links 来实现。Universal Links 通用链接是 Apple 在 2015 推出的一个新功能,只有在 iOS9 以上才支持。如果你的 App 支持 Universal Links,那就可以访问 HTTP/HTTPS 链接直接唤起 APP 进入具体页面,不需要其他额外判断;如果未安装 App,访问此通用链接时可以一个自定义网页。

关于如何添加 Universal Links 来唤醒 App,Apple 官方文档 Support Universal Links 中虽然有了说明,但是具体的细节操作却未交代清楚,致使我走了不少弯路。其实到最后发现具体实现其实很简单,大体来说分三步。

添加验证域名

激活 Xcode 工程中的 Associated Domains ,需要填入想要支持的域名,必须以 applinks: 为前缀,Apple 将会在合适的时候,从这个域名请求验证文件。

添加验证域名

上传验证文件

新建一个 json 格式的验证文件命名为 apple-app-site-association ,注意不要加 .json 后缀,然后编辑验证内容如下:

{
"applinks": {
"apps": [],
"details": [
{
"appID": "RFD4R6TMUT.org.reactjs.native.HAD",
"paths": ["/url"]
}
]
}
}

上面需要修改的地方只有 appIdpaths,其中 appIDTeamIdBundle Identifier 两部分相加组成,即 appID = TeamId.Bundle Identifier。进入 Apple Developer 网站,找到 Certificates, IDs & Profiles --> App IDs,查阅便可获得:

Apple Developer 获取 ID

如果上传成功后,可以进行先行在线验证

处理 URL 跳转

修改 AppDelegate.m,添加内容如下:

#import <React/RCTLinkingManager.h>
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}

App Links

在 Android 中,唤醒功能是通过 App Links 来实现。App Links 是谷歌在 Android M 之后推出的一个新功能,在此之前,点击一个链接会产生一个弹出框,询问用户打开哪个应用(包括浏览器应用)。但是谷歌在 Android M 实现了一个自动认证(auto-verify)机制,让开发者可以避开这个弹出框,使用户不必去选择一个列表,直接跳转到他们的 app。

App Links 和 Universal Links 实现大同小异,也是通过上传文件进行验证,在 app 第一次安装或更新后第一次打开时候,会自动下载服务器的文件然后进行验证。

在使用 App Links 之前,需要先激活,修改 AndroidManifest.xml,增加一个 intent-filter,设置自动验证 android:autoVerify="true",然后填写验证域名和需要唤醒的路径。这样 APP 就会自动在所列的 host 中去验证,如果验证成功,APP 将成为匹配 URI 默认打开方式。

<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<!-- 添加 intent-filter -->
<intent-filter android:autoVerify="true">
<!-- action 和 category 必须如下填写 -->
<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="airi.me"
android:pathPrefix="/url" />
</intent-filter>
</activity>

上传验证文件

新建一个验证文件命名为 assetlinks.json,编辑 assetlinks.json 如下:

;[
{
relation: ['delegate_permission/common.handle_all_urls'],
target: {
namespace: 'android_app',
package_name: 'com.had',
sha256_cert_fingerprints: ['C1:96:B8:EB:AC:BD:6C:B3:03:...:7E:13:CC:0B:EE:50:80:5D:DA:81']
}
}
]

其中需要修改的只有包名 package_namesha256_cert_fingerprints,其中包名在 AndroidManifest.xml 里可以找到,sha256_cert_fingerprints 需要在密钥里面获取。

在 Windows 上 keytool 命令放在 JDK 的 bin 目录中(比如 C:\Program Files\Java\jdkx.x.x_x\bin),需要在命令行中先进入那个目录才能执行此命令。

keytool -list -v -keystore had.jks

验证文件编辑上传完成后,可以先行在线验证

RN Linking 模块

React Native 通过 Linking 模块提供了一个通用的接口来与传入和传出的 App 链接进行交互。如果你的应用被其注册过的外部 url 调起,则可以在任何组件内获取和处理它:

// 组件内监听 Url 跳转
componentDidMount() {
Linking.getInitialURL()
.then(url => {
this.navigate(url)
})
.catch(console.error)
Linking.addEventListener('url', this.appWokeUp)
}
componentWillUnmount() {
Linking.removeEventListener('url', this.appWokeUp)
}
appWokeUp = event => {
this.navigate(event.url)
}
navigate = url => {
// dosomething
}

参考文章: 唤醒 APP 的那些事 Universal Linking For React-Native with Rails API, and Deep Linking Android Universal Links, URI Schemes, App Links, and Deep Links: What’s the Difference?