前言
如果你做广告变现或者用户分析相关功能,Google 正在用 Privacy Sandbox 把你过去十年的技术方案推翻。
Privacy Sandbox 不是"加个隐私弹窗"这么简单。它做的事情是:在系统层消除跨 App 追踪的技术基础。GAID(Google Advertising ID)会在用户关闭后失效,第三方 SDK 不再能直接跑在你的进程里读取 IMEI/MAC。
这一篇讲三个核心 API:Topics API(兴趣推断)、SDK Runtime(SDK 沙箱)、Attribution Reporting(归因报告),以及怎么适配。
一、Privacy Sandbox 整体架构
+------------------------------------------+
| 你的 App |
| +----------+ +------------------+ |
| | 业务代码 | | SDK Runtime Client| |
| +----------+ +--------+---------+ |
| | |
+---------------------------+---------------+
| IPC (跨进程)
+-----------+-----------+
| SDK Runtime 进程 |
| (沙箱,隔离你的 App) |
| +-----------------+ |
| | Third-party SDK | |
| +-----------------+ |
+-----------+-----------+
|
+-------------+-------------+
| Privacy Sandbox APIs |
| +--------+ +------------+ |
| | Topics | | Attribution | |
| +--------+ +------------+ |
| +-----------+ |
| | Protected | |
| | Audience | |
| +-----------+ |
+-----------------------------+
核心思路:SDK 不能直接访问用户数据,所有用户数据归系统管。SDK 只能通过系统 API 拿到"聚合后的、不可追踪到个人的"结果。
二、Topics API
Topics API 替代 GAID 做兴趣推断。系统根据用户最近使用的 App 推断出 5 个兴趣话题(Topics),广告 SDK 可以查询这些话题来匹配广告,但拿不到用户身份。
工作机制
1. 系统持续观察用户使用的 App
2. 每个周期(默认 7 天)计算 Top 5 Topics
3. 广告 SDK 调 getTopics() -> 拿到一个 Topic 列表
4. 同一周期内,同一 App 只能拿到一个 Topic(随机选)
5. 3 周后 Topic 过期
集成代码
// 广告 SDK 侧:获取 Topics
class AdManager(context: Context) {
private val topicsManager = context.getSystemService(TopicsManager::class.java)
suspend fun getTopics(): List<Topic> = suspendCoroutine { cont ->
val request = GetTopicsRequest.Builder()
.setAdsExtName("com.example.adnetwork") // 你的 SDK 包名
.build()
topicsManager.getTopics(
executor = Executors.newSingleThreadExecutor(),
callback = object : OutcomeReceiver<GetTopicsResponse, Exception> {
override fun onResult(result: GetTopicsResponse) {
cont.resume(result.topics)
}
override fun onError(error: Exception) {
cont.resume(emptyList())
}
},
input = request
)
}
}
返回的 Topic 对象包含:
taxonomyVersions: 分类版本号modelVersion: 模型版本id: 话题 ID(对应 IAB 分类)topicsVersion: 系统版本
你可以把 Topic ID 映射到广告投放策略,但不能拿它追踪用户。
隐私保证
不可链接:App A 拿到的 Topic 和 App B 拿到的不一样(同一周期内随机分配)
过期:3 周后旧 Topic 消失
可控:用户在系统设置里可以关闭 Topics,或查看哪些 App 查询过他的 Topics
三、SDK Runtime
SDK Runtime 把第三方 SDK 跑在一个独立进程里,跟你的 App 进程隔离。SDK 不能直接访问你的 App 内存、SharedPreferences、数据库。
契约式通信
你的 App 和 SDK 通过 AIDL 或 RemoteViews 通信:
// 1. SDK 侧:实现接口
class AdService : SandboxedSdkProvider() {
override fun onLoadSdk(params: LoadSdkParams): SandboxedSdk {
return SandboxedSdk(AdServiceImpl())
}
}
class AdServiceImpl : IAdService.Stub() {
override fun loadAd(adUnitId: String): AdResponse {
// 在 SDK 进程里执行,拿不到 App 的数据
return fetchAdFromServer(adUnitId)
}
}
// 2. App 侧:调用
class MainActivity : ComponentActivity() {
private lateinit var sdkSandboxManager: SdkSandboxManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sdkSandboxManager = getSystemService(SdkSandboxManager::class.java)
// 加载 SDK
val sandboxedSdk = sdkSandboxManager.loadSdk(
"com.example.adsdk",
Bundle()
)
// 拿到接口
val adService = IAdService.Stub
.asInterface(sandboxedSdk.getInterface())
// 调用
val ad = adService?.loadAd("banner_home")
}
}
实际影响
四、Attribution Reporting API
替代传统像素追踪和 MMP(AppsFlyer/Adjust)的设备指纹方案。
工作流程
1. 广告展示:App A 展示广告 -> 注册 Source(source_event_id)
2. 用户转化:用户安装 App B 并触发事件 -> 注册 Trigger(trigger_event_id)
3. 归因报告:系统匹配 Source 和 Trigger -> 生成归因报告
4. 报告发送:延迟发送给广告主服务器(加入噪声保护隐私)
代码示例
// 广告展示侧:注册 Source
class AdDisplayActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val manager = getSystemService(AttributionManager::class.java)
val sourceParams = SourceRegistrationRequest.Builder(
AttributionRegistrationRequest.SourceParams(
destination = "android-app://com.example.shopping",
sourceEventId = 12345L,
sourceType = SourceType.EVENT,
inputEvent = null
)
).build()
manager.registerWebSource(
request = sourceParams,
executor = mainExecutor,
callback = object : OutcomeReceiver<Unit, Exception> {
override fun onResult(result: Unit) { /* 注册成功 */ }
override fun onError(error: Exception) { /* 失败 */ }
}
)
}
}
// 转化侧:注册 Trigger
class ConversionActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val manager = getSystemService(AttributionManager::class.java)
val triggerParams = AttributionRegistrationRequest.TriggerParams(
destination = "android-app://com.example.advertiser",
triggerEventId = 67890L,
triggerData = "purchase".toByteArray()
)
manager.registerTrigger(triggerParams)
}
}
隐私保护机制
延迟报告:归因报告不是实时发送,有 24-48 小时延迟
噪声注入:报告里加入随机假数据,无法精确追踪单个用户
预算限制:每个 Source 的 Trigger 匹配次数有上限
数据汇总:不支持精确的事件级归因,只支持聚合维度
五、适配建议
广告变现 App:迁移到 Topics API + Attribution Reporting,保留 GAID 作为 fallback(用户未关闭时)
分析 SDK:评估是否需要迁移到 SDK Runtime。如果 SDK 需要读取 App 数据,迁移成本很高
时间线:
• 2024-2025:Privacy Sandbox API 稳定版可用
• 2026:Android 15+ 开始限制 GAID 可用性
• 2027+:可能完全弃用 GAID
兼容方案:
fun getAdvertisingId(context: Context): String? {
// 先尝试 GAID
val adId = AdvertisingIdClient.getAdvertisingIdInfo(context)
if (adId.id != "00000000-0000-0000-0000-000000000000") {
return adId.id // 用户未关闭 GAID
}
// fallback: Topics API
return null // 用 Topics 代替
}
小结
Privacy Sandbox 重构的是广告变现的技术底座。短期影响小(GAID 还在),但中期(2026-2027)会直接影响收入。做广告变现的团队需要提前评估迁移成本,特别是 SDK Runtime 的跨进程改造。
下篇讲 Android 音频系统,从 AudioTrack 到 Oboe,低延迟音频怎么做。