在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,等同于为某个用户设定的专属链接。