oynix

于无声处听惊雷,于无色处见繁花

Unity中处理多平台的Deep Link

在Android,或者是iOS上,打开应用一般分为两种方式,一种是在通过桌面上的应用图标打开,另一种是通过其他应用跳转至目标应用,而后者多是通过URI的形式。

比较常见的场景是,通过短信、邮件等形式,向用户下发一个活动信息、优惠信息的链接,用户点击这个链接后会打开一个网页,在这个网页上会跳转到目标应用,以此来增加DAU。

1. URI组成

一个完整的URI链接有如下几个部分:

scheme://host:port/path?k1=v1&k2=v2

schema为协议名称,host是主机名,port是端口号,path是资源路径,问号后面的是查询参数。

2. 如何跳转

当点击一个http开头的链接地址后,默认会使用浏览器打开这个链接,有些应用则不会跳转,而是在自身内部打开这个链接,因为其内置了一个浏览器,可以处理这个http开头的链接。

但并不是每个链接都是由http开头,只是它比较常见。比如,我完全可以自定义一个链接,

happy://every.day

如上,那么该如何解析呢?

点击http的链接之所以会跳转到浏览器,是因为浏览器类的应用在系统中注册了对http这种scheme的解析处理,所以当需要处理http开头的URI链接时,系统会打开浏览器。对于一个URI,如果系统查不到可以处理的应用,那么结果就是无法接处理;如果查询到了多个,比如装了两个浏览器应用,那么就会弹个窗出来,让你选择使用哪个。

同样,我们也可以在系统中注册对scheme的解析。Android应用是在AndroidManifest.xml文件中增加Intent Filter,iOS应用则是在Info.plist文件中增加URL Types。

回到最初的使用场景,既然想通过一个URI进入到自己的应用,那么就需要注册一个独一无二的scheme,这样才可以避免点击链接的时候,误入其他应用。

3. 注册scheme

传统方式是在Unity Editor中导出项目之后,在项目中再手动添加,但每导出一次就要添加一次。当然也可以在Unity中设置好,这样导出之后就不用再次添加,Android项目可以直接打包。

Android

在Player Settings中的Publish Settings里,将Build下的Custom Main Manifest选项打开,不同版本的Editor可能会有不同,这样在Assets/Plugins/Android下就会生成一个AndroidManifest文件,将想要处理的scheme以Intent Filter添加进去即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools">
<application>
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<intent-filter>

<data
android:host="every.day"
android:scheme="happy" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<action android:name="android.intent.action.VIEW" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
</manifest>

iOS

位置在Player Settings中的Other Settings里,有一个Supported URL Schemes,这里直接写自己想要处理的scheme即可,这样在导出时,就会自动添加到Xcode工程下的Info.plist文件中。

如此一来,我们的应用便在系统中注册了具备处理happy的scheme的能力。

4. 触发跳转

happy://every.day

还是这个URI,这里需要注意的是,我们是要解析我们自定义的scheme,让系统来判断这个URI到底交给哪个应用来处理,所以不可以直接复制到浏览器的地址栏,这样跳过了选应用这一步,而是直接交给浏览器处理了,而且浏览器是无法处理happy这个shceme的。

常见方式是,把这个URI放到一个网页上,用JS来触发系统对这个URI的处理,这样做还有个好处是,可以同时处理多个平台,Android和iOS的用户可以使用同一个网页,在网页上再去区分不同的平台,比如,当跳转到应用失败时,可以根据平台跳转到不同的应用商店页面,引导应用下载、安装。

多个平台、多个操作,一个网页,统一处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<script>
var linkDownloadAppStore = '{AppStoreLink}';
var linkDownloadPlayMarket = '{GooglePlayLink}';
var linkIntentUri = 'happy://every.day';

var uagent = navigator.userAgent.toLowerCase();

var isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);

function functionOpen() {
if (uagent.search('iphone') > -1 || uagent.search('ipad') > -1 || uagent.search('ipod') > -1 || isIOS) {
setTimeout(function () { window.location = linkDownloadAppStore; }, 14000);
window.location.href = linkIntentUri;
} else if (uagent.search('android') > -1) {
var elsShowIOS = document.getElementsByClassName('show-ios');
for (var i = 0; i < elsShowIOS.length; i++) {
elsShowIOS[i].style.display = 'none';
}

setTimeout(function () { window.location = linkDownloadPlayMarket; }, 14000);
window.location.href = linkIntentUri;
} else{
var elDownloadMobile = document.getElementById('download-links-mobile');
elDownloadMobile.style.display = 'none';

var elDownloadPC = document.getElementById('download-links-pc');
elDownloadPC.style.display = 'block';
}
}

function functionDownload() {
var linkDownload = linkDownloadPlayMarket;

if (uagent.search('iphone') > -1 || uagent.search('ipad') > -1 || uagent.search('ipod') > -1) {
linkDownload = linkDownloadAppStore;
} else if (uagent.search('android') > -1) {
linkDownload = linkDownloadPlayMarket;
}
window.location.href = linkDownload;
}

</script>

5. 接收URI数据

上面已经说了如何在系统中注册,已经如何通过自定义的scheme打开应用,下面来说在应用中如何接收URI,以及所携带的数据。

假设,群发给用户一个URI链接,凡是通过点击这个链接打开应用的用户,可以获得20金币的奖励,那么URI就可以这么写:

happy://every.day?coin=20

这个完整的链接,会被存储在Application.absoluteURL这个变量中,如果这个字符串不为空,则说明用户是通过URI进入应用的。

开头有说过,启动应用分为两种情况,一种是冷启动,一种是热启动。如果某次热启动时通过URI,那么Application.absoluteURL的值就会更新成最新的,可通过下面这个事件来注册Application.absoluteURL的变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private string DeepLink;

private void Awake()
{
DontDestroyOnLoad(this);

Application.deepLinkActivated += HandleDeepLink;
DeepLink = Application.absoluteURL;
}

private void HandleDeepLink(string uri)
{
DeepLink = uri;
}

通过解析URI,就可以获得金币,以及金币的数量。

当然,这个链接只是举个例子,实际中,每个链接都要加多维度的限制,同时还需要加密处理,不然可能内就会存在大量非常规数据。比如,限制链接的起止时间、领取次数、奖励类型、奖励数量,甚至限制用户ID,等同于为某个用户设定的专属链接。

------------- (完) -------------
  • 本文作者: oynix
  • 本文链接: https://oynix.com/2023/06/3ab8ef24552d/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

欢迎关注我的其它发布渠道