使用Android App Links实现深度链接

解读

在国内 Unity 手游/数字孪生项目里,深度链接(Deep Link) 是拉新、召回、裂变、支付回调、房间邀请的“刚需”。
Android App Links(API 23+)是 Google 官方推荐的 HTTPS 方案,相比旧 scheme 方式,它能 一键直达、无弹窗、防劫持、可统计,但国内渠道包多、指纹证书杂、HTTPS 证书链常不完整,面试时考官想确认:

  1. 你是否真的在 Unity 侧落地过 aar+gradle+manifest 的完整链路;
  2. 能否把 UnityPlayerActivity 的 onNewIntent 数据安全地送到 C# 层;
  3. 是否理解 数字指纹与 assetlinks.json 的双向校验 在国内云厂商 CDN 下的坑点;
  4. 能否在 热更框架(HybridCLR/Addressables) 里做到“链接即资源”,而不重启客户端。

知识点

  1. Android App Links 与 Scheme Deep Link 区别:前者强制 HTTPS + 数字指纹,后者任意自定义 scheme,易被浏览器拦截。
  2. Unity 导出 Gradle 工程结构:launcher + unityLibrary,修改 launcher/src/main/AndroidManifest.xmlandroid:autoVerify="true"intent-filter 的 android.intent.action.VIEW 类别
  3. assetlinks.json 部署:必须放在 https://<domain>/.well-known/assetlinks.json,内容包含 sha256_cert_fingerprints,国内注意 CDN 301 跳转、缺少 Content-Type=application/json 导致验证失败。
  4. 签名指纹获取:使用 keytool -list -v -keystore xxx.keystore,但国内渠道(华为、OV、小米)会二次签名,需把 渠道公钥指纹 一起写进 assetlinks,否则只能走 fallback browser。
  5. Unity 侧接收:继承 UnityPlayerActivity 重写 onCreate/onNewIntent,通过 Intent.getData() 拿到 Uri,用 UnityPlayer.UnitySendMessage("DeepLinkManager", "OnRawUrl", uri.toString()) 抛到 C#;注意 主线程切换JsonUtility 解析 query 参数
  6. 防重复启动:在 android:launchMode="singleTask" 下,若游戏已存活,系统会走 onNewIntent,此时需在 C# 层做 队列去重场景幂等 处理。
  7. 国内兼容:微信/QQ 内置浏览器会屏蔽第三方 intent,需同时接入 Universal Links(iOS)+ App Links(Android)+ 兜底 H5 中转页,并埋点统计 uv→scheme→apk 安装→首次打开 的漏斗。
  8. 热更新结合:利用 Addressables 的 ContentUpdatePath,把 link 参数作为 catalog 版本 key,实现“链接即更新”,避免整包重启。

答案

(按面试口语化,分五步,关键数字与文件用加粗

第一步:在 Unity 2021.3.16f1 导出 Gradle 工程,勾选 Custom Main Manifest,在 launcher/AndroidManifest.xmlUnityPlayerActivity 节点下新增:

<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="u3d.example.com" android:pathPrefix="/open"/> </intent-filter>

第二步:把 release.keystoresha256指纹 写入 assetlinks.json,样例:

[{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.example.u3d", "sha256_cert_fingerprints": ["FA:C6:17:E5:..."] } }]

文件部署到 https://u3d.example.com/.well-known/assetlinks.json**,**HTTP 状态 200Content-Type=application/json无 301 跳转;国内阿里云 CDN 需在 边缘规则 关闭强制跳转 HTTPS。

第三步:Unity 侧新建 DeepLinkManager.cs,挂到 GameEntry 场景单例:

public void OnRawUrl(string raw) { var uri = new System.Uri(raw); var query = System.Web.HttpUtility.ParseQueryString(uri.Query); string roomId = query.Get("roomId"); if(!string.IsNullOrEmpty(roomId)) EventBus.RoomJoin(roomId); }

第四步:Android 插件 DeepLinkActivity.java 继承 UnityPlayerActivity,在 onNewIntent 中:

Uri data = intent.getData(); if(data != null) UnityPlayer.UnitySendMessage("GameEntry", "OnRawUrl", data.toString());

并在 onCreate 里同样调用一次,覆盖 冷启动 场景。

第五步:验证

  1. adb shell am start -W -a android.intent.action.VIEW -d "https://u3d.example.com/open?roomId=123" com.example.u3d
  2. 日志出现 I/Unity: OnRawUrl https://u3d.example.com/open?roomId=123
  3. 手机 设置→默认打开方式 里显示 已验证(无“是否打开”弹窗)。

完成以上五步,即实现 零弹窗、防劫持、可热更 的 Android App Links 深度链接。

拓展思考

  1. 如果项目出海,需要 一次链接多端直达,可把 Firebase Dynamic Links 作为 云托管的 assetlinks 代理,但国内网络需 接入香港节点,否则 GMS 抽风 导致验证失败。
  2. 对于 数字孪生大屏 项目,链接参数往往包含 sceneName、cameraPose、timestamp,可在 ScriptableObject 里做 URI→Scene 映射表,利用 Addressables.LoadSceneAsync 实现 秒级切换,并把 camera 矩阵 通过 Base64 压缩 进 query,≤512 字节 可躲过部分国产浏览器截断。
  3. HybridCLR 热更脚本 已更新,而 原生 AndroidManifest 未变 时,assetlinks.json 新增指纹强制用户重装 才能生效;此时可在 H5 中转页 里用 wasm-unity-loader 启动 WebGL 兜底体验,同时 后台静默下载 apk,实现 “链接→WebGL 试玩→后台升级→下次打开即新包” 的平滑过渡。