Skip to main content

Deep Link

  • 通过 URI 将用户重定向到应用内特定内容
  • Android 6.0+(API 级别 23)支持 Android App Links(带自动校验的 web link),通过 autoVerify 属性,系统将自动校验应用是否符合 intent-filter 中指定的 host 网域
  • 玩家可通过手动修改系统设置来选择该链接类型的默认处理应用

步骤 1:配置 SDK

在 AndroidManifest.xml 中添加 intent-filter

  • scheme:协议类型(如 lipasshttphttps
  • host:网域(如 appwww.levelinfinite.com
  • pathPrefix(可选):页面路径前缀,用于区分多渠道包

自定义协议:

<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="sdk-deeplink-confs.intlgame.com"
android:pathPrefix="/channelA" />
</intent-filter>

HTTP/HTTPS Web Link:

<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="https"
android:host="www.levelinfinite.com"
android:pathPrefix="/app" />
</intent-filter>

Android App Link(Android 6.0+,带自动校验):

<intent-filter android:autoVerify="true">
<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="www.levelinfinite.com"
android:pathPrefix="/app" />
</intent-filter>

处理 Intent 数据:

MainActivityonCreateonNewIntent 中获取 deep link 数据。

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Intent intent = getIntent();
Uri data = intent.getData();

if (data != null) {
INTLSDK.Deeplink.receive(data.toString());
}
}

public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
INTLSDK.onNewIntent(intent);

Uri appLinkData = intent.getData();
if (appLinkData != null) {
INTLSDK.Deeplink.receive(appLinkData.toString());
}
}

部署 assetlinks.json 文件至:

https://domain.name/.well-known/assetlinks.json

文件格式:

[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example",
"sha256_cert_fingerprints": ["您的_SHA256_指纹"]
}
}]

生成 SHA-256 指纹:

keytool -list -v -keystore my-release-key.keystore

多商店渠道包配置(可选)

  • 渠道包:同一应用的不同 APK,包名不同,用于不同分发渠道
  • pathPrefix:用于区分渠道,保证点击深度链接时打开正确 APK

配置示例:

[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.puppies.app",
"sha256_cert_fingerprints": ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.monkeys.app",
"sha256_cert_fingerprints": ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
}]

步骤 3:测试

  • 在 Android 文本编辑器(如便签)中点击链接
  • 检查日志中是否包含:[ (intl_deeplink_manager.cpp:37) Receive]
android-deeplink
  • 使用 adb 测试链接跳转:
  adb shell am start -W -a android.intent.action.VIEW -d "lipass://app/xxxxxx" com.example.android_app
note

调用网页 URI 时,Android 系统会依序尝试执行下列操作,直到请求成功为止:

  1. 如果已指定该链接类型的默认处理应用,则打开此应用
  2. 打开唯一可以处理该链接类型的应用
  3. 允许玩家从对话框中选择应用打开

微信限制:微信内置浏览器中的链接不会跳转应用


步骤 1:配置 Web 服务器

在 Web 服务器根目录部署 apple-app-site-association 文件,路径:

https://domain.name/.well-known/apple-app-site-association

{
"applinks": {
"apps": [],
"details": [{
"appID": "TEAMID.com.your.bundleid",
"paths": ["/app/*"]
}]
}
}

关键要点:

  • apps 必须是空数组
  • appID = Team ID + Bundle ID
  • paths 支持通配符(* 和 ?)
  • 使用 "NOT " 前缀排除路径(如:"NOT /videos/wwdc/2010/*"
  • 路径字符串必须区分大小写
  • 系统按指定顺序评估路径,找到匹配项后停止
  • 重签名应用:必须将重签名证书的 appIDpaths 也添加到配置中

路径配置示例:

  • 整个网站:"*"
  • 特定链接:"/wwdc/news/"
  • 网站某部分:"/videos/wwdc/2015/*"
  • 组合通配符:"/foo/*/bar/201?/mypage"

步骤 2:启用 Associated Domains

在 Apple 开发者账号中:

  • 启用 Associated Domains 能力
  • 重新生成描述文件
adp-associated-domain

在 Xcode 中:

  • 添加 Associated Domains 能力
  • 添加域名:applinks:your_link(无需 https://)
adp-associated-domain

或通过代码配置:

PostProcess 中添加以下代码:

#if UNITY_2019_3_OR_NEWER
var capManager = new UnityEditor.iOS.Xcode.ProjectCapabilityManager(projPath, entitlementsFilePath, targetGuid: targetProjectName);
#else
var capManager = new UnityEditor.iOS.Xcode.ProjectCapabilityManager(projPath, entitlementsFilePath, targetProjectName);
#endif

capManager.AddAssociatedDomains(new string[] { "applinks:your_link" });

步骤 3:配置 SDK

由于当前 INTLCore 对该入口方法 hook 不生效,所以在 Unity 的 UnityAppController.mm 文件的 UnityAppController 类中添加 iOS 的生命周期入口函数。

目前 XCodePostProcess.cs 代码已经自动在编译后的处理流程中添加了 application:continueUserActivity:restorationHandler 方法(函数中不需要额外添加代码)。

note

不要在 UnityAppController 的子类中添加生命周期入口函数。确保入口函数是在跟 application:openURL:options: 等系统生命周期函数在同一个类中。

- (BOOL)application:(UIApplication *)application 
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler {
return YES;
}

步骤 4:测试

  • 在 iOS 备忘录中点击链接
  • 应打开应用并记录日志:INTLApplicationDelegate:continueUserActivity
note

若配置正确但链接不生效:尝试重启设备或重新安装应用

安装或更新后首次打开应用时:

  1. App 向配置的域名发起 GET 请求拉取 apple-app-site-association 文件
  2. App 将文件注册到系统
  3. WebView 打开 URL 时根据注册的配置检查 URL
  4. 命中注册的 universal link 则打开应用触发 delegate
  5. 未命中则 WebView 继续跳转 URL

微信限制:微信内置浏览器中的链接不会跳转应用

分享链接

跨平台归因链接,支持深度链接和延迟深度链接

功能说明:

  • 深度链接(Deep linking):应用已安装,直接打开应用内特定位置
  • 延迟深度链接(Deferred deep linking):应用未安装,先跳转应用商店下载,安装后打开时跳转到特定位置

步骤 1:在 AppsFlyer 控制台创建应用

根据 AppsFlyer 文档 添加应用程序 来创建您的应用程序。

分享链接 分享链接
  • Android:在 "Use App Links" 添加 SHA-256 指纹, 并保存生成的 intent-filter 代码
分享链接

步骤 3:配置 SDK

Android:

  1. INTLConfig.ini 中添加:
   APPSFLYER_APP_INVITE_ONELINK_ID_ANDROID = {模板_ID}
  1. [Android LifeCycle] 中添加 AppsFlyer(如缺失):
   LIFECYCLE = WeChat,QQ,Twitter,Adjust,Facebook,Google,Line,VK,Garena,Discord,Dmm,Update,Firebase,WhatsApp,Permission,GooglePGS,AppsFlyer
  1. AndroidManifest.xml 的游戏 MainActivity 中添加 intent-filter 用于打开游戏,并替换 {Subdomain}{OneLink_Template_ID}
    <activity
android:name="com.intlgame.unity.MainActivity"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="{Subdomain}.onelink.me"
android:pathPrefix="/{OneLink_Template_ID}"
android:scheme="https" />
</intent-filter>
</activity>

iOS:

  1. INTLConfig.ini 中添加:
   APPSFLYER_APP_INVITE_ONELINK_ID_IOS = {模板_ID}
  1. 添加子域名到 Associated Domains:
    • Unity:修改 XCodePostProcess.cs,添加
    capManager.AddAssociatedDomains(new string[] { "appslinks:subdomain.onelink.me"});
    • UE:在 DefaultEngine.iniAssociatedDomains 中添加子域名:
    AssociatedDomains=appslinks:subdomain.onelink.me

设置 OneLink 回调:

INTLAPI.AddExtendResultObserver(OnExtendEvent); // 设置回调

public void OnExtendEvent(INTLExtendResult extendResult)
{
if ("OnOneLinkResult".Equals(extendResult.ExtendMethodName)) // OneLink 点击回调
{
if (extendResult.RetCode == 0) // OneLink 点击回调成功
{
string jsonParameter = extendResult.ExtraJson; // 点击 OneLink 后 AF 返回的参数
// TODO: Handle OneLink Parameter
}
else // OneLink 点击回调失败
{
// TODO: handle OneLink Failed result
}
}
}

设置被分享链接的用户点击生成的 OneLink 后的结果回调:

生成 OneLink 和点击 OneLink 共用一个回调,通过回调中的 INTLExtendResult 的参数区分。

    INTLAPI.AddExtendResultObserver(OnExtendEvent);//设置回调
public void OnExtendEvent(INTLExtendResult extendResult)
{
if ("OnOneLinkResult".Equals(extendResult.ExtendMethodName))//OneLink 点击回调
{
if (extendResult.RetCode == 0)//OneLink 点击回调成功
{
string jsonParameter = extendResult.ExtraJson;//点击 OneLink 后 AF 返回的参数
//TODO:Handle OneLink Parameter
}
else//OneLink 点击回调失败
{
//TODO: handle OneLink Failed result
}
}
}

参考示例代码生成 OneLink,并按实际情况修改参数。:

StringBuilder sb = new StringBuilder();
sb.Append("{");
sb.Append("\"deep_link_value\":\"abc\"").Append(","); //<TARGET_VIEW>

sb.Append("\"deep_link_sub1\":\"1234\"").Append(",");//<PROMO_CODE>
sb.Append("\"deep_link_sub2\":\"1234\"").Append(",");//<REFERRER_ID(openid)>
sb.Append("\"channel\":\"mobile_share\"").Append(",");//Channel
sb.Append("\"campaign\":\"summer_sale\"");//Campaign
//其他参数,可选
sb.Append("\"deep_link_sub3\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub4\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub5\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub6\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub7\":\"1234\"").Append(",");
sb.Append("\"deep_link_sub8\":\"1234\"").Append(",");

sb.Append("\"af_sub4\":\"12324\"").Append(",");
sb.Append("\"af_sub5\":\"dfasdf\"").Append(",");

sb.Append("}");
String paramsJsonString = sb.ToString();
INTLExtend.Invoke("AppsFlyer", "generateInviteLinkUrl", paramsJsonString);

步骤 5:测试集成

  1. 进入 AppsFlyer 控制台。
  2. 在左侧边栏中,单击 SDK Integration Tests
  3. 选择要测试的应用程序,点击 Run test
分享链接
  1. Run non-organic install test 页面,选择测试设备并选择 Other
  2. 扫描二维码并安装应用。
分享链接
note

若要再次测试激活,请删除该应用程序并重新启动测试。


客户端接口

API函数定义
SetDeepLinkObserver设置 deep link 回调,通知游戏引擎层是否有数据到达,收到回调后,需要手动调 Fetch 函数获取数据。
RemoveDeepLinkObserver移除 deep link 的回调

Fetch

Fetch 接口获取 SDK 缓存的 deep link 数据。更多信息,请参见:

API函数定义
Fetch获取 SDK 缓存的 deep link 数据。