移动 SDK深度链接
Android
AdStage DeepLink 集成指南(Android)
目录
概述
AdStage DeepLink SDK 提供以下功能:
- 实时深度链接:通过 URL Scheme、App Link 立即处理
- 延迟深度链接:应用安装后首次启动时恢复
- 动态创建深度链接:通过服务器 API 创建可跟踪的链接
- 归因跟踪:基于 UTM 参数的营销分析
项目设置
添加 Gradle 依赖
settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://repo.nbase.io/repository/nbase-releases") }
}
}build.gradle.kts (Module: app)
dependencies {
implementation("io.nbase:nbase-adapter-adstage:3.0.9")
// 必需依赖
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
}AndroidManifest.xml 设置
1. 权限设置
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 必需权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Install Referrer 权限(用于延迟深度链接) -->
<uses-permission android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE" />
<application>
<!-- ... -->
</application>
</manifest>2. DeepLink Intent Filter 设置
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask">
<!-- 默认启动器 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- URL Scheme 深度链接(例:myapp://promo/summer) -->
<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="myapp"
android:host="promo" />
</intent-filter>
<!-- App Link (https://go.myapp.com/...) -->
<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="go.myapp.com" />
</intent-filter>
<!-- AdStage 深度链接域名(例:https://go.adstage.net/...) -->
<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="go.adstage.net" />
</intent-filter>
</activity>3. launchMode 设置的重要事项
<!-- 推荐:singleTask -->
android:launchMode="singleTask"
<!-- 使用 singleTask 时:
- 若已存在 Activity 则复用
- 会调用 onNewIntent()
- 移动到 Task 最上层
-->Application 类设置
MyApplication.kt
package com.example.myapp
import android.app.Application
import io.nbase.adapter.adstage.AdStage
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 初始化 AdStage SDK
AdStage.initialize(
context = this,
apiKey = "your-api-key-here",
serverUrl = "https://api.adstage.app" // 可选项,也可使用默认值
)
// 设置深度链接监听器
setupDeeplinkListener()
}
private fun setupDeeplinkListener() {
AdStage.setDeeplinkListener(object : io.nbase.adapter.adstage.models.DeeplinkListener {
override fun onDeeplinkReceived(data: io.nbase.adapter.adstage.models.DeeplinkData) {
android.util.Log.d("AdStage", """
✅ 收到深度链接
- Short Path: ${data.shortPath}
- Deeplink ID: ${data.deeplinkId}
- Source: ${data.source}
- Parameters: ${data.parameters}
""".trimIndent())
// 进行全局处理,或通过事件总线传递
handleGlobalDeeplink(data)
}
override fun onDeeplinkFailed(error: String, shortPath: String?) {
android.util.Log.e("AdStage", """
❌ 深度链接失败
- Error: $error
- Short Path: $shortPath
""".trimIndent())
}
})
}
private fun handleGlobalDeeplink(data: io.nbase.adapter.adstage.models.DeeplinkData) {
// 通过全局事件总线或 SharedFlow 传递给当前 Activity
// 或保存到深度链接管理器后由 Activity 取用
}
}在 AndroidManifest.xml 中注册 Application
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.MyApp">
<!-- ... -->
</application>MainActivity 设置
MainActivity.kt
package com.example.myapp
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import io.nbase.adapter.adstage.AdStage
import io.nbase.adapter.adstage.models.DeeplinkData
import io.nbase.adapter.adstage.models.DeeplinkListener
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 处理深度链接(应用首次启动或从后台启动时)
handleIntent(intent)
// 设置本地深度链接监听器(可选项)
setupLocalDeeplinkListener()
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent) // 重要:替换为新的 Intent
Log.d(TAG, "onNewIntent called")
// 处理深度链接(应用已在运行时收到新的深度链接)
handleIntent(intent)
}
private fun handleIntent(intent: Intent?) {
if (intent == null) {
Log.w(TAG, "Intent is null")
return
}
Log.d(TAG, """
Intent received:
- Action: ${intent.action}
- Data: ${intent.data}
- Extras: ${intent.extras?.keySet()?.joinToString()}
""".trimIndent())
// 将 Intent 传递给 AdStage SDK
val handled = AdStage.handleIntent(this, intent)
if (handled) {
Log.i(TAG, "✅ AdStage 已处理该深度链接")
} else {
Log.d(TAG, "ℹ️ 这不是 AdStage 的深度链接")
// 处理普通 Intent
handleRegularIntent(intent)
}
}
private fun handleRegularIntent(intent: Intent) {
when (intent.action) {
Intent.ACTION_VIEW -> {
// 处理普通网页链接或自定义 scheme
val uri = intent.data
Log.d(TAG, "Regular deep link: $uri")
}
// 处理其他 action...
}
}
/**
* 本地深度链接监听器(仅在 Activity 中处理)
* 若已在 Application 中设置全局监听器,则此项为可选
*/
private fun setupLocalDeeplinkListener() {
AdStage.setDeeplinkListener(object : DeeplinkListener {
override fun onDeeplinkReceived(data: DeeplinkData) {
Log.d(TAG, "📱 在 Activity 中收到深度链接: ${data.shortPath}")
// 处理业务逻辑
when {
data.parameters.containsKey("campaign") -> {
handleCampaignDeeplink(data)
}
data.parameters.containsKey("promo") -> {
handlePromoDeeplink(data)
}
else -> {
handleDefaultDeeplink(data)
}
}
}
override fun onDeeplinkFailed(error: String, shortPath: String?) {
Log.e(TAG, "❌ 深度链接失败: $error")
// 显示错误 UI
}
})
}
private fun handleCampaignDeeplink(data: DeeplinkData) {
val campaign = data.parameters["campaign"]
val channel = data.parameters["channel"]
Log.d(TAG, """
🎯 处理广告系列深度链接
- Campaign: $campaign
- Channel: $channel
""".trimIndent())
// 跳转到广告系列页面
// startActivity(Intent(this, CampaignActivity::class.java).apply {
// putExtra("campaign", campaign)
// })
}
private fun handlePromoDeeplink(data: DeeplinkData) {
val promoCode = data.parameters["promo"]
Log.d(TAG, "🎁 推广代码: $promoCode")
// 应用推广
// applyPromoCode(promoCode)
}
private fun handleDefaultDeeplink(data: DeeplinkData) {
Log.d(TAG, "📋 处理默认深度链接: ${data.shortPath}")
// 保持主页面或跳转到特定页面
}
override fun onDestroy() {
super.onDestroy()
// 清理监听器(防止内存泄漏)
// 若使用全局监听器,则不要在此处 clear
// AdStage.clearDeeplinkListener()
}
}深度链接接收处理
1. 实时深度链接(应用运行中)
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AdStage.setDeeplinkListener(object : DeeplinkListener {
override fun onDeeplinkReceived(data: DeeplinkData) {
// data.shortPath: "SDGWNBB"
// data.deeplinkId: "507f1f77bcf86cd799439011"
// data.source: DIRECT_LINK 或 INSTALL_REFERRER
// data.eventType: OPEN 或 INSTALL
// data.parameters: Map<String, String>
// 提取跟踪参数(以服务器响应的键为准)
val channel = data.parameters["channel"]
val subChannel = data.parameters["subChannel"]
val campaign = data.parameters["campaign"]
// 提取自定义参数
val customParam = data.parameters["customKey"]
// 页面跳转
navigateToScreen(data)
}
override fun onDeeplinkFailed(error: String, shortPath: String?) {
// 错误处理
showErrorDialog(error)
}
})
}
}2. 延迟深度链接(应用安装后首次启动)
// 自动处理!
// 在 AdStage.initialize() 时会自动查询 Install Referrer,
// 若存在已保存的深度链接,则会自动调用 onDeeplinkReceived
// 需要手动处理时:
AdStage.handleInstallReferrer(context)3. 区分深度链接来源
import io.nbase.adapter.adstage.models.DeeplinkSource
override fun onDeeplinkReceived(data: DeeplinkData) {
when (data.source) {
DeeplinkSource.DIRECT_LINK -> {
Log.d(TAG, "🔗 直接点击深度链接(实时)")
// 可立即切换页面
}
DeeplinkSource.INSTALL_REFERRER -> {
Log.d(TAG, "📦 延迟深度链接(Play Store Install Referrer)")
// 例如在引导流程结束后切换页面
}
}
}创建深度链接
1. Request 对象方式
import io.nbase.adapter.adstage.AdStage
import io.nbase.adapter.adstage.models.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class DeeplinkManager {
fun createSimpleDeeplink() {
CoroutineScope(Dispatchers.Main).launch {
try {
val request = CreateDeeplinkRequest(
name = "夏季推广链接",
description = "2024 年夏季推广",
channel = "google-ads",
campaign = "summer2024",
parameters = mapOf(
"promo" to "SUMMER20",
"discount" to 20
)
)
val response = AdStage.createDeeplink(request)
Log.d(TAG, """
✅ 深度链接创建完成
- Short URL: ${response.shortUrl}
- Short Path: ${response.shortPath}
- ID: ${response.id}
""".trimIndent())
// 分享创建好的 URL
shareUrl(response.shortUrl)
} catch (e: Exception) {
Log.e(TAG, "❌ 深度链接创建失败: ${e.message}")
}
}
}
}2. Builder 模式(DSL 风格)
fun createDeeplinkWithBuilder() {
CoroutineScope(Dispatchers.Main).launch {
try {
val response = AdStage.createDeeplink("冬季推广") {
description("2024-2025 冬季推广")
shortPath("WINTER24") // 用户指定的路径
// 跟踪参数
channel("facebook-ads")
subChannel("instagram")
campaign("winter2024")
adGroup("fashion-lovers")
creative("banner-001")
content("hero-image")
keyword("winter-sale")
// 重定向设置
redirectConfig {
type(RedirectType.APP) // STORE, APP, WEB
// Android 设置
android {
appScheme("myapp://promo/winter")
packageName("com.example.myapp")
webUrl("https://example.com/promo/winter")
}
// iOS 设置
ios {
appScheme("myapp://promo/winter")
bundleId("com.example.myapp")
appStoreId("123456789")
webUrl("https://example.com/promo/winter")
}
// 桌面端设置
desktop {
webUrl("https://example.com/promo/winter")
}
}
// 自定义参数
parameter("discount", 30)
parameter("promoCode", "WINTER30")
parameter("validUntil", "2025-03-31")
// 状态设置
status(DeeplinkStatus.ACTIVE)
}
Log.d(TAG, "✅ Short URL: ${response.shortUrl}")
} catch (e: Exception) {
Log.e(TAG, "❌ 错误: ${e.message}")
}
}
}3. 回调方式(异步)
fun createDeeplinkWithCallback() {
val request = CreateDeeplinkRequest(
name = "应用邀请链接",
channel = "referral",
campaign = "invite-friend",
parameters = mapOf(
"referrer" to "USER123",
"bonus" to 5000
)
)
// 回调方式需将 suspend 函数包装在协程中使用
CoroutineScope(Dispatchers.Main).launch {
try {
val response = AdStage.createDeeplink(request)
onSuccess(response)
} catch (e: Exception) {
onError(e)
}
}
}
private fun onSuccess(response: CreateDeeplinkResponse) {
Log.d(TAG, "深度链接创建成功: ${response.shortUrl}")
// 更新 UI
runOnUiThread {
textView.text = response.shortUrl
shareButton.isEnabled = true
}
}
private fun onError(error: Exception) {
Log.e(TAG, "深度链接创建失败: ${error.message}")
runOnUiThread {
Toast.makeText(this, "深度链接创建失败", Toast.LENGTH_SHORT).show()
}
}4. RedirectType 说明
enum class RedirectType {
STORE, // 应用未安装时 → 跳转到商店
// 应用已安装时 → 启动应用(延迟深度链接)
APP, // 应用未安装时 → 跳转到网页回退 URL
// 应用已安装时 → 启动应用(实时深度链接)
WEB // 始终跳转到网页 URL
// 与应用是否安装无关
}5. 深度链接分享示例
fun shareDeeplink(shortUrl: String) {
val shareIntent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, """
🎁 特别推广邀请!
通过此链接注册即可获得 5,000 韩元折扣券。
$shortUrl
""".trimIndent())
}
startActivity(Intent.createChooser(shareIntent, "邀请好友"))
}高级功能
1. 设置全局用户属性
// 在 Application onCreate() 中设置
val userAttributes = UserAttributes(
gender = "male",
country = "KR",
city = "Seoul",
age = "28",
language = "ko-KR"
)
AdStage.setUserAttributes(userAttributes)
// 之后调用 trackEvent 时会自动包含这些属性2. 查询与重置用户属性
// 查询当前已设置的用户属性
val current: UserAttributes? = AdStage.getUserAttributes()
// 登出时移除用户属性
AdStage.clearUserAttributes()
// 重置所有全局信息(移除用户属性)
AdStage.clearAll()设备信息(型号、OS 版本、应用版本、广告标识符等)与会话由 SDK 自动 收集与管理。无需另行设置设备信息或调用会话管理 API。
问题排查
1. 收不到深度链接
检查清单:
- AndroidManifest.xml 中的 intent-filter 是否正确设置
- 确认已设置
android:exported="true" - 建议设置
android:launchMode="singleTask" - 确认已调用 AdStage.initialize()
- 确认已调用 setDeeplinkListener()
- 确认已调用 handleIntent()
调试:
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.d(TAG, """
Intent Debug:
- Action: ${intent?.action}
- Data: ${intent?.data}
- Scheme: ${intent?.data?.scheme}
- Host: ${intent?.data?.host}
- Path: ${intent?.data?.path}
""".trimIndent())
handleIntent(intent)
}2. 延迟深度链接不生效
原因:
- 没有 Install Referrer 权限
- 并非通过 Google Play Store 安装(直接安装 APK)
- Install Referrer API 初始化失败
解决方法:
// 手动处理 Install Referrer
AdStage.handleInstallReferrer(applicationContext)
// 确认日志
Log.d(TAG, "Install Referrer 处理完成")3. App Link 验证失败
确认方法:
# 1. 确认 Digital Asset Links 文件
https://go.myapp.com/.well-known/assetlinks.json
# 2. 使用验证工具
https://developers.google.com/digital-asset-links/tools/generator
# 3. 用 ADB 测试
adb shell am start -a android.intent.action.VIEW -d "https://go.myapp.com/ABCDEF"assetlinks.json 示例:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myapp",
"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"
]
}
}]4. ProGuard/R8 混淆问题
proguard-rules.pro:
# AdStage SDK
-keep class io.nbase.adapter.adstage.** { *; }
-keepclassmembers class io.nbase.adapter.adstage.** { *; }
# 模型类
-keep class io.nbase.adapter.adstage.models.** { *; }
# OkHttp
-dontwarn okhttp3.**
-keep class okhttp3.** { *; }
# Kotlin Coroutines
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
5. 多进程环境
<!-- 存在运行于独立进程的 Activity 时 -->
<activity
android:name=".SomeActivity"
android:process=":separate">
<!-- 若该 Activity 也要处理深度链接,则需要另行初始化 -->
</activity>// 需要在每个进程中调用 AdStage.initialize()
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 在所有进程中初始化
AdStage.initialize(this, apiKey)
}
}测试方法
1. ADB 测试
# URL Scheme 测试
adb shell am start -a android.intent.action.VIEW -d "myapp://promo/summer"
# App Link 测试
adb shell am start -a android.intent.action.VIEW -d "https://go.myapp.com/ABCDEF"
# 包含参数
adb shell am start -a android.intent.action.VIEW -d "myapp://promo?campaign=summer&discount=20"2. 确认 Intent 日志
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.let {
Log.d(TAG, "=== Intent Debug ===")
Log.d(TAG, "Action: ${it.action}")
Log.d(TAG, "Data: ${it.data}")
Log.d(TAG, "Extras: ${it.extras?.keySet()?.joinToString()}")
it.data?.let { uri ->
Log.d(TAG, "URI Scheme: ${uri.scheme}")
Log.d(TAG, "URI Host: ${uri.host}")
Log.d(TAG, "URI Path: ${uri.path}")
Log.d(TAG, "URI Query: ${uri.query}")
}
}
}3. 真机测试
// 创建测试用深度链接
CoroutineScope(Dispatchers.Main).launch {
val response = AdStage.createDeeplink("测试链接") {
channel("test")
campaign("test-campaign")
parameter("test", "true")
}
Log.d(TAG, "测试 URL: ${response.shortUrl}")
// 复制该 URL,在浏览器或即时通讯应用中测试
}
