Android 基础 API
val sdk = TrackBusinessSDK.createAsync(applicationContext, endpoint, projectKey)
sdk.track("checkout_started")
sdk.identify("user_1001")
sdk.screen("Checkout")Android SDK 使用 Kotlin 实现,也提供 Java 宿主可调用的公开 API。
TIP
获取核心 AAR 和扩展模块的离线集成包,请前往 Android SDK 下载页。快速接入指南请参考 Android 快速起步。
常用接口
| 模块类别 | 核心入口 (Kotlin) | 核心入口 (Java) |
|---|---|---|
| 初始化 | TrackBusinessSDK.createAsync(...) | TrackBusinessSDK.createAsync(..., callback) |
| 配置构建 | TrackBusinessConfig(...) | TrackBusinessConfig.builder()...build() |
| 业务埋点 | sdk.track(...) / sdk.group(...) | sdk.track(...) / sdk.group(...) |
| 位置快照 | trackWithLocation / grantLocationConsent / revokeLocationConsent | Map + LocationSnapshot + 撤销回调;完整语义 |
| 手动 Flush | sdk.flush() | sdk.flush() |
| 身份管理 | identify / clearUser / switchUser / signOut | 同左 |
| 身份快照 | val id = sdk.identity | IdentityState id = sdk.getIdentity() |
| 用户属性 | sdk.setUserProperties(...) / profileIncrement | 同左 (强类型 Map) |
| 健康诊断 | sdk.getHealthSnapshot() / exportDiagnostics() | 同左 |
Kotlin 和 Java 均可使用公开事件接口。Java 宿主使用 Builder 配置和回调初始化。队列写入由 SDK 异步处理;track() 返回不代表事件已写入磁盘或被接收端确认。验证方法见数据校验。
1. SDK 初始化与生命周期
基础初始化 (TrackBusinessSDK.createAsync)
在应用启动时异步创建 SDK,并在应用范围内复用实例:
val sdk = TrackBusinessSDK.createAsync(
context = applicationContext,
endpoint = "https://collector.example.com/v1/events/batch",
projectKey = "android-public-project-key",
)
sdk.track("checkout_started", mapOf("checkoutId" to "chk_01"))TrackBusinessSDK.createAsync(
getApplicationContext(),
"https://collector.example.com/v1/events/batch",
"android-public-project-key",
TrackBusinessConfig.builder().build(),
(sdk, error) -> {
if (error != null) {
android.util.Log.w("Analytics", "SDK initialization failed");
return;
}
sdk.track("checkout_started", java.util.Collections.singletonMap("checkoutId", "chk_01"));
}
);初始化失败时,Kotlin 抛出异常,Java 回调返回错误。同步 create() 仍可用于后台线程。完整宿主示例见快速接入。
配置参数项 (TrackBusinessConfig)
下表列出常用配置;Kotlin 使用命名参数,Java 使用同名 Builder 方法。
| 字段 | 类型 | 默认值 | 作用与范围 |
|---|---|---|---|
storageNamespace | String? | null | 默认沿用单实例存储。多项目/环境共存时显式设置不同命名空间 |
initialConsent | ConsentDecision | granted("MINIMAL") | pending()、granted(level) 或 denied(),见授权与隐私 |
flushAt | Int | 20 | 达到队列条数阈值后调度发送;限制为 1–100 |
flushIntervalSeconds | Long | 30 | 前台发送周期,正值限制为 5–120 秒;非正值关闭定时器 |
flushOnBackground | Boolean | true | 退后台时安排发送,系统可能延迟执行 |
flushOnNetworkReachable | Boolean | true | 网络恢复时安排发送 |
flushOnIdentityChange | Boolean | true | 身份变化时安排发送 |
trackAppLifecycle | Boolean | true | 注册应用生命周期采集,仍受事件授权门控 |
trackApplicationVersion | Boolean | true | 授权允许时检测安装/更新版本变化 |
activityLifecycleOptions | ActivityLifecycleIntegration.Options | Options() | Activity 页面与冷启动深链接选项,按宿主场景配置 |
identitySyncTransport | IdentitySyncTransport? | null | 可选身份同步适配器,需配套服务 |
deviceIdentifierReader | DeviceIdentifierReader? | null | 可选外部标识读取器,需宿主授权与依赖 |
beforeSend | EventInterceptor? | null | 入队前处理事件属性或拒绝事件 |
eventEnrichers | List<EventEnricher> | emptyList() | 命名业务属性增强;Java 使用 Builder 同名方法和回调 |
policy | SDKPolicy | SDKPolicy() | 插件准入及白名单;Java Builder 提供 pluginEnabled / pluginAllowlist 便捷方法 |
integrationWarningListener | IntegrationWarningListener? | null | 接收事件拒绝原因等元数据,不包含属性、身份值或凭据 |
配置在创建实例时确定。队列上限、加密等设置见包内 TrackBusinessConfig。默认敏感字段保护始终执行;自定义业务字段仍须由宿主审查。
命名业务属性增强
支持 event_enrichment 的 SDK 包可在普通及远程策略工厂的 TrackBusinessConfig 中安装增强器。Java 使用 EventEnricher.create(name, handler);回调接收只读业务字段和事件元数据,返回 EventEnrichmentPatch.properties(map) 或 EventEnrichmentPatch.of(context, properties)。返回 null 表示不修改,不会删除事件。
配置列表按顺序处理,同名增强器保留第一个,同名业务属性由后者覆盖。回调必须是短时同步操作;SDK 隔离异常和超限补丁,但不能抢占同步死循环。保留的身份、Session、eventId 和 SDK Context 不能改写。默认补丁预算 16,384 bytes,最终仍执行隐私和事件计划检查。
注册不提升 Consent;默认 MINIMAL 不满足示例中的 STANDARD。完整 Java/Kotlin 注册、授权、触发及释放流程见客户端插件与能力接入。单个拦截/过滤需求仍可使用 beforeSend。
异步远程策略工厂 (createWithRemotePolicy)
当服务端已经部署并验证 GET /v1/sdk/policy,且宿主主动选择远程控制面时,可使用异步工厂下发插件开关、采样率或 Tracking Plan(仅 Kotlin 协程)。它不是基础初始化或上传的前置条件;没有该接口时使用普通 create 和可选的本地 Tracking Plan。Android 只在 SDK 创建前加载远程策略,运行期间不会热刷新;策略变化需由宿主重新创建 SDK 后生效。
val resolver = RemotePolicyResolver(
provider = UrlConnectionRemotePolicyProvider(
URL("https://collector.example.com/v1/events/batch"),
"android-public-project-key",
),
cache = hostPolicyCache,
verifier = policyVerifier,
requireSignature = true, // 生产环境强制验签
)
val sdk = TrackBusinessSDK.createWithRemotePolicy(
context = applicationContext,
endpoint = "https://collector.example.com/v1/events/batch",
projectKey = "android-public-project-key",
config = TrackBusinessConfig(initialConsent = ConsentDecision.granted("STANDARD")),
resolver = resolver,
)WARNING
远程策略请求涉及网络通信,调用前宿主必须确认已处于权威的 GRANTED 授权状态;若处于 PENDING 或 DENIED,工厂将直接以 CONSENT_REQUIRED_BEFORE_REMOTE_POLICY 失败中断。
实例释放 (close)
sdk.close()- 停止实例资源:停止前台定时调度和 Activity 生命周期监听;已提交给 Android 系统的 WorkManager 任务仍按系统调度执行,
close()不表示取消这些任务; - 数据保留:不会清空本地 Durable Outbox 或加密 SharedPreferences 中的身份状态;使用相同存储作用域重新
create后可恢复待处理状态。
2. 身份识别与生命周期 (Identity)
核心身份流转 API
// 登录后绑定业务身份
sdk.identify("user_10086")
// 退出登录
sdk.signOut()
// 只清除当前用户,保留匿名身份
sdk.clearUser()
// 切换账号
sdk.switchUser("user_10087")
// 绑定其他业务标识
sdk.bindIdentity("crm_id", "crm_8899")
// 重置身份和会话
sdk.reset()
// 获取当前身份
val identity = sdk.identity // Java: sdk.getIdentity()reset() 会重置用户与会话身份;安装身份按 SDK 语义保留。切换账号请使用 switchUser()。
CAUTION
当 SDK 当前已绑定某一 userId 时,直接调用 identify("other_user") 将抛出 IDENTITY_USER_SWITCH_REQUIRES_RESET 异常以防止身份串联。请在切换账号时明确使用 switchUser 或先执行 signOut。
3. 事件采集
SDK 在事件属性进入隐私过滤前限制嵌套层数、值数量和文本总量。采集属性或事件拦截器返回的属性超过处理预算时,SDK 丢弃该事件,诊断记录 invalid_event(event_input_budget)。后续事件继续处理。
业务事件与群组 (track / group)
// 1. 自定义事件(支持嵌套 Map 与标准类型)
sdk.track("checkout_started", mapOf(
"checkoutId" to "chk_9981",
"estimatedTotal" to 199.0,
"isVip" to true,
))
// 2. B2B 群组归属关联
sdk.group("org_alibaba", mapOf(
"role" to "admin",
"department" to "tech",
))Map<String, Object> props = new HashMap<>();
props.put("checkoutId", "chk_9981");
props.put("estimatedTotal", 199.0);
props.put("isVip", true);
sdk.track("checkout_started", props);
sdk.group("org_alibaba", Collections.singletonMap("role", "admin"));设置全局属性或会话属性时,若更新后的总量超出 SDK 处理预算,本次更新会被拒绝,已有属性保持原值。健康快照记录 event_input_budget。
beforeSend、事件增强和动态公共属性回调在发起采集的线程上同步执行。回调应只做短小的内存操作,避免网络请求、磁盘读取和跨线程等待。回调执行期间,SDK 会拒收其他线程提交的事件,诊断记录 before_send_busy。回调结束后恢复正常准入。
4. 属性管理 (Super & User Properties)
SDK 属性合并优先级遵循:Global 超级属性 $\rightarrow$ Session 属性 $\rightarrow$ 动态属性 $\rightarrow$ 事件显式属性(后者覆盖前者)。
Session Properties
生命周期与当前会话(Session)绑定,会话过期或调用 signOut() / reset() 时自动清空:
sdk.setSessionProperties(mapOf("campaign" to "summer_2026"))
sdk.removeSessionProperties(listOf("campaign"))Dynamic Super Properties
在事件生成时刻动态求值(必须保持高执行效率与同步非阻塞):
sdk.registerDynamicSuperProperties {
mapOf(
"currentTab" to currentSelectedTab,
"cartCount" to getCartBadgeCount().toString(),
)
}
sdk.clearDynamicSuperProperties()sdk.registerDynamicSuperProperties(() -> {
Map<String, String> dynamic = new HashMap<>();
dynamic.put("currentTab", "home");
return dynamic;
});用户 Profile 操作
Profile API 提交操作事件,本地缓存与服务端画像可能不同步。服务端是否执行操作及如何保证并发更新,由部署实现决定。
setUserPropertyOnce由服务端判断目标属性是否已存在;profileAppend不保证数组去重或并发原子性。unsetUserProperties可指定未出现在本地缓存中的属性名。clearUserProperties按本地已知键产生$profile_unset,针对本地已知属性,不表示删除所有服务端数据。- 未授权、opt-out 或关闭实例后不执行 Profile 修改,也不在恢复授权后补发。禁止持久存储时仅维护内存缓存。
- 缓存读写问题通过诊断查看;操作返回不代表服务端更新完成。隐私擦除使用
denyConsent或clearLocalData,检查完成结果,清理失败时不能恢复采集。
// 1. 覆盖设置属性
sdk.setUserProperties(mapOf("membership" to "gold", "level" to "5"))
// 2. 仅在首次设置(服务端已有时忽略)
sdk.setUserPropertyOnce(mapOf("firstChannel" to "google_play"))
// 3. 数值累加
sdk.profileIncrement(mapOf("loginCount" to 1.0, "points" to 50.0))
// 4. 数组追加(单次最多 100 项,每个字符串最多 256 字符)
sdk.profileAppend(mapOf("tags" to listOf("sports", "music")))
// 5. 删除指定属性,或清理本地已知的画像属性
sdk.unsetUserProperties(listOf("legacy_flag"))
sdk.clearUserProperties()
// 6. 获取本地 Profile 缓存快照
val profileMap = sdk.getUserPropertiesMap()业务 Item 维度属性
// 设置商品/物料属性
sdk.itemSet("product", "sku_10086", mapOf("title" to "SDK 入门手册", "price" to "59.0"))
// 删除指定物料
sdk.itemDelete("product", "sku_10086")5. 存储与发送
当前策略关闭持久存储时,获授权事件使用当前进程的内存队列;进程结束后未发送事件无法恢复。
手动刷新调度 (flush)
sdk.flush()- 非阻塞调用:立即触发内部调度器执行出队与网络上报,但不会在调用者线程阻塞等待 I/O;
- 使用建议:SDK 按配置合批发送,默认阈值为 20 条、前台周期为 30 秒,并在切后台时尝试发送;无需在每次
track()后调用flush()。
6. 调试、诊断与监控 (Diagnostics)
// 1. 获取完整健康快照
val health = sdk.getHealthSnapshot()
// 2. 导出诊断报告;分享前检查事件 ID、事件名等自定义关联字段
val diagnosticsReport = sdk.exportDiagnostics()
// 3. 获取有界诊断时间线事件(最近 50 条生命周期变更)
val timeline = sdk.getDiagnosticsTimeline(50)
// 4. 查看最近入队的事件元数据(不含属性明细)
val recentEvents = sdk.getRecentEvents()
// 5. 开发构建可在 TrackBusinessConfig 注册接入提示
val config = TrackBusinessConfig(
integrationWarningListener = IntegrationWarningListener { warning ->
Log.w("TrackBusiness", "${warning.code}: ${warning.eventName}")
},
)7. 屏幕浏览与原生交互
页面停留与加载耗时
// 1. 显式声明页面浏览 ($screen)
sdk.screen("CheckoutScreen", mapOf("source" to "cart"))
// 2. 页面加载完成耗时 ($page_load)
sdk.recordPageLoad(
pageViewId = "pv_home_01",
durationMs = 380L,
screenName = "HomeActivity",
)控件点击与语义交互 (recordAppClick)
用于在 Native 点击监听回调中精确上报,无需插桩 View 树:
sdk.recordAppClick(
action = "checkout_submit",
targetTag = "button",
trackId = "btn-submit-order",
captureKey = "android:view:checkout-submit", // 稳定的采集标识
elementType = "Button",
screenName = "CheckoutScreen",
)8. 平台原生能力与生命周期
Activity 页面与 App Link 自动化
通过 ActivityLifecycleIntegration.Options 精确配置:
val config = TrackBusinessConfig(
initialConsent = ConsentDecision.granted("STANDARD"),
activityLifecycleOptions = ActivityLifecycleIntegration.Options(
allowedActivityNames = setOf(MainActivity::class.java.name, CheckoutActivity::class.java.name),
trackScreenLeaves = true, // 开启页面停留自动统计 ($page_leave)
screenNameResolver = { activity -> activity.localClassName },
coldStartDeepLinkActivityNames = setOf(MainActivity::class.java.name),
coldStartDeepLinkHosts = setOf("links.example.com"),
),
)Deep Link 接收与归因
- 推荐(ComponentActivity):引入
track-business-sdk-android-lifecycle自动监听onNewIntent:kotlinval deepLinkBinding = ComponentActivityDeepLinkIntegration.install(this, sdk) - 原生传统 Activity:在
onNewIntent中显式转交:kotlinoverride fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) sdk.recordDeepLinkIntent(intent) }
install() 将 ACTION_VIEW 热启动 Intent 转交 SDK,不应用冷启动 Host 白名单。需要限制目标时,应由宿主检查 scheme 与 host 后手工调用 recordDeepLinkIntent;两种转发方式选择其一。
渠道来源快照
sdk.recordAttribution(mapOf("utm_source" to "newsletter", "utm_campaign" to "spring"))
sdk.track("order_created", mapOf("orderType" to "subscription"))仅需要已有首次/最近 UTM 来源时使用 recordAttribution。需要当前入口、会话入口和排除返回域时,见下方 recordAcquisition。
入口与营销触点(recordAcquisition,可选)
需要区分当前入口、会话入口和最近有效触点时使用。复用已初始化的 SDK,在宿主确认 STANDARD 授权及新入口事实之后调用:
import com.trackbusiness.sdk.models.AcquisitionInput
sdk.recordAcquisition(
AcquisitionInput.Builder(mapOf("utm_source" to "newsletter"))
.landingUrl("https://shop.example.com/landing")
.build()
)
sdk.track("entry_viewed", mapOf("surface" to "campaign_landing"))新输入被本地接受后,后续事件可携带 context.campaign.entry、sessionEntry 和 latestTouch。它们表示入口事实,不计算订单归因。原接口 recordAttribution 继续维护已有 UTM 视图,不要为同一入口重复调用两个接口。
应用安装与版本更新
// 1. 显式记录安装事件(内部去重,重复调用不产生重复事件)
sdk.recordApplicationInstall(mapOf("installer" to "google_play"))
// 2. 显式记录版本升级
sdk.recordApplicationUpdate("2.1.0", mapOf("currentBuild" to "210"))异常与性能诊断上报
// 1. 非致命业务异常;使用受控分类,不传原始异常内容
sdk.recordNonFatalError("PaymentGatewayTimeout")
// 2. 启动耗时与网络指标
sdk.recordStartupPerformance(durationMs = 450L)
sdk.recordNetworkPerformance(durationMs = 120L, resourceCount = 3, transferBytes = 8192L, failedCount = 0)9. 元素曝光 (Exposure)
手动曝光 API (recordExposure)
记录 $sdk_exposure 事件:
sdk.recordExposure(
exposureId = "item_card_998",
durationMs = 800L,
visibleRatio = 0.75,
)sdk.recordExposure("item_card_998", 800L, 0.75);曝光事件支持附加业务属性;自定义属性名不要以 $ 开头,并遵循隐私过滤及事件大小限制。请确认接收服务支持对应事件契约。
原生 View 与 RecyclerView 绑定
请由宿主在视图生命周期内持有返回的绑定对象,并在不再使用时调用 detach() 或 unbind()。使用列表绑定时,宿主模块还需声明自己的 androidx.recyclerview:recyclerview 依赖。
// 1. 普通 View 扩展
val cardExposure = binding.recommendCard.trackBusinessExposure(
sdk = sdk,
exposureId = "card_recommend",
minDurationMs = 500L,
minVisibleRatio = 0.5,
)
// 2. RecyclerView 列表滚动曝光
val feedExposure = RecyclerViewExposureBinder(sdk) { position -> "feed_item_$position" }
.minDurationMs(500L)
.minVisibleRatio(0.5)
.bind(binding.feedRecyclerView)// Java 适配器
ViewExposureTracker cardExposure = ViewExposureTracker.attach(
binding.recommendCard,
sdk,
"card_recommend",
ViewExposureOptions.builder().minDurationMs(500L).minVisibleRatio(0.5).build()
);10. 外部设备标识读取 (GAID / OAID)
外部标识用于辅助归因,SDK 默认不读取,也不将其作为业务主身份。
GMS 与 MSA 适配器注入
按实际分发环境选择一个读取器;下例使用按顺序尝试的组合。宿主需自行提供相应第三方依赖、MSA 证书及适用授权。用户限制广告标识时,组合不会继续回退以绕过限制。
val sdk = TrackBusinessSDK.create(
context = applicationContext,
endpoint = endpoint,
projectKey = projectKey,
config = TrackBusinessConfig.Builder()
// 示例:Google Play Services 优先,MSA OAID 作为广告用途的后备。
.deviceIdentifierReader(FallbackDeviceIdentifierReader(listOf(
GooglePlayServicesDeviceIdentifierReader(applicationContext),
MsaOaidDeviceIdentifierReader(applicationContext, "msa-cert.pem"),
)))
.build(),
)
// 从协程调用;选择与宿主授权一致的用途。
suspend fun readMeasurementIdentifier(sdk: TrackBusinessSDK) =
sdk.readDeviceIdentifier(DeviceIdentifierPurpose.MEASUREMENT)只使用 Google Play Services 时,可将上面的组合替换成 GooglePlayServicesDeviceIdentifierReader(applicationContext)。MSA OAID 只支持广告用途;读取失败或无可用标识时应允许业务继续,不要将标识原文写入日志。
11. 预置上下文规范 (NativeContextCollector)
获准采集的事件可包含以下环境上下文。具体字段取决于设备和系统是否提供相关信息:
| 字段命名空间 | 包含属性 | 采集规则与说明 |
|---|---|---|
context.device | manufacturer, model, osVersion, screenWidth, screenHeight, screenScale, orientation, locale, timezone, type | 屏幕尺寸以 dp 呈现;设备类型归纳为 phone / tablet / tv;SDK 不读取 IMEI、MAC 或硬件序列号。 |
context.app | id, name, version, build | 分别取宿主 App 的包名、应用展示名、versionName 与 versionCode。 |
context.network | online, connectionType | 网络连通性与类型(wifi / cellular / ethernet / 5g / 4g 等);不采集运营商私有标识或 WiFi SSID。 |
事件的网络属性来自后台刷新的一秒内快照。快照未就绪或过期时,本次事件省略网络属性。
应用版本与构建观测
sdk.recordApplicationUpdate("2.1.0", mapOf("currentBuild" to "210"))SDK 会读取应用版本及构建号。显式调用时可通过 currentBuild 提供构建号。事件上的 $previous_version、$current_version、$previous_build、$current_build 和 $change_type 由 SDK 确定,不能通过自定义属性覆盖。
首次观测只建立基线。后续版本变化生成 version_changed,同版本的已知构建号变化生成 build_changed。SDK 不通过版本字符串排序判断升级或降级;缺失构建号不填零。内存模式下的基线不保证跨进程保留。
网关探测
持久实例可调用 probeGateway(timeoutMs)(Kotlin)或 probeGateway(timeoutMs, callback)(Java)探测已配置事件 Endpoint 所属 Gateway 的健康状态。静态 TrackBusinessSDK.DurableClient.probeEndpoint 用于指定事件 Endpoint;两者均请求同一 Gateway origin 下的 GET /health,不会对 POST /v1/events/batch 写入路由发送 GET。探测不采集事件,也不证明 Collector 已接受业务数据;事件是否被接受仍以批量上传的 ACK 为准。
GatewayProbeResult.errorMessage 使用固定的探测错误描述,不返回原始网络异常文本;宿主需要结合 httpStatusCode 判断结果。Java 静态探测最多通知一次,宿主回调异常会被隔离;协程取消保留取消语义,不伪装成网络故障。
实例所有权与控制记录提交
同一进程中,相同 endpoint、projectKey 和 storageNamespace 只能持有一个活动 Runtime。重复创建会抛出 SDK_SCOPE_ALREADY_OWNED;宿主应复用已有实例,或等待 close() 完成后再创建。不同存储作用域可独立使用。SDK 不提供 SharedPreferences 跨进程事务保证。
SDK 计时器 startTimer/endTimer 的输出字段为 properties.$timer_duration_ms(非负毫秒数),宿主 durationMs 属性不被覆盖。计时事件的严格 Tracking Plan 需声明该测量字段。
有界投递验证与 Java 结构化属性
// 显式开发期操作;不替换原有 flush() 调度。
val report = sdk.verifyDelivery(timeoutMs = 3000)Map<String, Object> properties = new HashMap<>();
properties.put("amount", 12.5);
properties.put("paid", true);
properties.put("items", Arrays.asList("one", "two"));
sdk.trackProperties("checkout", properties);
sdk.verifyDelivery(3000L, (report, error) -> {
if (report != null && "acknowledged".equals(report.getStatus())) {
// 本次非空固定范围均收到合法整批 ACK。
}
});trackProperties 接受 String、Boolean、Byte/Short/Integer/Long、有限 Float/Double、null、嵌套 Map 和 List;它不调用任意对象的 toString()。转换边界为最多 1000 个节点、8 层、键长 128、字符串长 65536;超限、循环引用、不支持的类型或非有限数值会本地拒收并记入 Diagnostics。属性仍通过现有事件、隐私和 Tracking Plan 校验,不绕过原有限制。原有 track(String, Map<String,String>) 保持兼容。
verifyDelivery 返回固定范围的准确确认/拒收/未知数量;空队列、超时、调度成功都不是 ACK。Java 回调不会阻塞调用线程;Kotlin suspend 入口对 Java 隐藏。详见可靠投递。