什么是 GAID 以及如何以编程方式获取它?Google 广告标识符 (GAID) 是由 Google Play 服务提供的一个可由用户重置的软件标识符,用于营销效果衡量。在确保合并后的应用清单文件包含所需的 AD_ID 权限后,可通过 AdvertisingIdClient API 在主线程之外以编程方式检索该标识符。
Google 广告标识符 (GAID),也称为 Android 广告标识符 (AAID),是由 Google Play 服务提供的一个由用户重置的唯一字符串,用于 Android 设备上的营销归因、个性化广告定位和数据统计。目标定位为 Android 13(API 级别 33)或更高版本的应用在访问 Google Play 服务广告标识符时,必须确保合并后的清单文件中包含
com.google.android.gms.permission.AD_ID权限,否则 API 将返回全零值。
| 术语 | 定义 |
|---|---|
| GAID | 由 Google Play 服务提供的 Google 广告标识符,用于广告效果衡量。 |
| AdvertisingIdClient | 用于查询广告标识符元数据的 Google Play 服务 API。 |
| AD_ID 权限 | 目标定位 API 级别 33+ 的应用在访问 Google Play 服务广告标识符时所需的 Android 清单权限。 |
| 全零标识符 | 当广告标识符不可用或被隐藏时返回的全零占位值(00000000-0000-0000-0000-000000000000)。 |
什么是 GAID 以及 Google 广告标识符如何运作
广告标识符的技术解析
Google 广告标识符 (GAID) 由 Google Play 服务作为不透明的标识符字符串返回,通常格式为 128 位小写十六进制 UUID(例如 38400000-8cf0-11bd-b23e-10b96e4ef00d)。应用程序应将返回的字符串视为不透明标识符,而不是根据其格式推断业务逻辑。
与硬件级标识符不同,GAID 在设计时融入了明确的隐私控制:
- 可由用户重置:用户可以随时通过设备设置生成新的随机标识符。
- 可由用户删除:从 Android 12 开始,用户可以删除其广告标识符,从而指示 Google Play 服务对所有应用隐藏广告标识符。
- 设备配置文件作用域:广告标识符通常在同一设备用户资料的所有应用之间可用,但同一物理设备上的不同 Android 用户或访客配置文件会收到不同的广告标识符。
Google Play 服务在管理标识符状态中的角色
GAID 不属于核心开源 Android 框架的一部分。它是一个由专有 Google Play 服务客户端库(com.google.android.gms:play-services-ads-identifier)动态管理和提供的应用级服务。
当应用程序调用 Google Play 服务时,客户端会与 Google Play 服务执行阻塞通信,以检索当前的广告标识符和用户限制广告跟踪偏好。
GAID 为什么取代硬件标识符
在早期 Android 版本中,广告网络和开发人员使用不可重置的硬件标识符来跟踪转化事件:
- 国际移动设备识别码 (IMEI):绑定到设备的蜂窝硬件。
- 媒体访问控制 (MAC) 地址:绑定到网络接口硬件。
- ANDROID_ID (SSAID):在现代 Android 8.0+ 版本中,此标识符的作用域限定为应用签名密钥、用户和设备的组合,而不是充当通用的跨应用广告密钥。
对于 Google Play 上的营销场景,开发人员必须在可用时使用广告标识符,而不是替代其他持久性设备标识符。
另请参阅:GAID ──> 移动归因架构
Android 13 权限要求:AD_ID 清单声明
API 级别 33 强执行模型
从 Android 13(API 级别 33)开始,Google 引入了 com.google.android.gms.permission.AD_ID 权限:
- 目标 SDK 33 及更高版本:目标定位为 Android 13 或更高版本的应用必须在其清单中声明
AD_ID权限。如果省略该权限,Google Play 服务将返回一串零(00000000-0000-0000-0000-000000000000)。 - 目标 SDK 32 及更低版本:清单中不需要
AD_ID权限。但是,由于用户的广告标识符隐私设置(包括限制广告跟踪或删除状态),广告标识符仍可能不可用或归零。 - Play 商店政策声明:在提交目标定位 API 级别 33+ 的更新时,开发人员必须在 Google Play Console 中完成当前的广告标识符声明,准确报告应用是否以及如何使用该标识符。

配置 AndroidManifest.xml 和 Gradle 依赖项
要声明权限,请在 AndroidManifest.xml 中包含 <uses-permission> 标签。在应用模块的 build.gradle 文件中,添加官方的 play-services-ads-identifier 依赖项。
以下配置演示了清单权限语法和 Gradle 依赖项设置:
```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<!-- 在 API 33+ 上访问非零 GAID 所必需 -->
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
<application
android:allowBackup="true"
android:label="@string/app_name"
android:theme="@style/Theme.MyApp">
<!-- 应用组件 -->
</application>
</manifest>
// 位置:app/build.gradle
// 注意:此处显示的是示例依赖项版本;在投入生产使用之前,请在官方仓库中验证当前的稳定版本。
dependencies {
// 官方 Google Play 服务广告标识符库
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
// 用于主线程之外异步执行的 Kotlin 协程
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
}
理解清单合并:传递性 SDK 注入
在构建 Android APK 或 Android App Bundle (AAB) 时,Gradle 构建系统会将所有导入的第三方库中的清单合并到一个最终的 AndroidManifest.xml 中。
如果导入的广告网络 SDK、统计工具或归因库在其内部清单中声明了 com.google.android.gms.permission.AD_ID,则该权限会自动合并到最终构建中。开发人员可以检查 app/build/outputs/logs/manifest-merger-release-report.txt 以审核是哪个依赖项引入了该权限。
以编程方式检索:在 Kotlin 中通过 AdvertisingIdClient 获取 GAID
强制的主线程外执行规则
调用 AdvertisingIdClient.getAdvertisingIdInfo(context) 会发起对 Google Play 服务的同步调用。Google 明确禁止在主(UI)线程上执行此方法。
在主线程上调用 getAdvertisingIdInfo() 会立即抛出 IllegalStateException:
java.lang.IllegalStateException: Calling this from your main thread can lead to deadlock
为防止应用出现 ANR(应用无响应)错误和运行时崩溃,开发人员必须使用 Dispatchers.IO 在后台工作线程或 Kotlin 协程内部调度提取调用。

使用 Kotlin 协程实现后台工作线程任务
以下的 Kotlin 实现演示了一个线程安全的 AdvertisingIdManager,它在主线程之外执行提取,处理 Google Play 服务可用性异常,并验证标识符的完整性:
package com.example.myapp.analytics
import android.content.Context
import android.util.Log
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
import com.google.android.gms.common.GooglePlayServicesRepairableException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
data class GaidResult(
val gaid: String?,
val isLimitAdTrackingEnabled: Boolean,
val isZeroed: Boolean,
val isAvailable: Boolean
)
object AdvertisingIdManager {
private const val TAG = "AdvertisingIdManager"
private const val ZEROED_GAID = "00000000-0000-0000-0000-000000000000"
/**
* 在主执行线程之外以编程方式检索 Google 广告标识符。
* 自动处理异常、全零状态和用户选择退出。
*/
suspend fun fetchGaid(context: Context): GaidResult = withContext(Dispatchers.IO) {
try {
// 在 IO 协程调度程序上调用阻塞的 Google Play 服务 IPC
val adInfo: AdvertisingIdClient.Info = AdvertisingIdClient.getAdvertisingIdInfo(context)
val rawId = adInfo.id
val isLAT = adInfo.isLimitAdTrackingEnabled
val isZeroed = rawId == null || rawId.equals(ZEROED_GAID, ignoreCase = true)
if (isZeroed) {
Log.w(TAG, "广告标识符不可用或为全零状态。")
GaidResult(
gaid = null,
isLimitAdTrackingEnabled = isLAT,
isZeroed = true,
isAvailable = false
)
} else {
Log.d(TAG, "成功检索到广告标识符。已启用 LAT:$isLAT")
GaidResult(
gaid = rawId,
isLimitAdTrackingEnabled = isLAT,
isZeroed = false,
isAvailable = true
)
}
} catch (e: GooglePlayServicesNotAvailableException) {
Log.e(TAG, "此设备上未安装 Google Play 服务或不可用。", e)
GaidResult(gaid = null, isLimitAdTrackingEnabled = true, isZeroed = false, isAvailable = false)
} catch (e: GooglePlayServicesRepairableException) {
Log.w(TAG, "Google Play 服务需要更新或用户恢复。", e)
GaidResult(gaid = null, isLimitAdTrackingEnabled = true, isZeroed = false, isAvailable = false)
} catch (e: IOException) {
Log.e(TAG, "连接到 Google Play 服务时发生通信错误。", e)
GaidResult(gaid = null, isLimitAdTrackingEnabled = true, isZeroed = false, isAvailable = false)
} catch (e: IllegalStateException) {
Log.e(TAG, "检索广告标识符时出现非法状态。", e)
GaidResult(gaid = null, isLimitAdTrackingEnabled = true, isZeroed = false, isAvailable = false)
} catch (e: Exception) {
Log.e(TAG, "检索 Google 广告标识符时发生意外错误。", e)
GaidResult(gaid = null, isLimitAdTrackingEnabled = true, isZeroed = false, isAvailable = false)
}
}
}
解析 AdvertisingIdClient.Info 并处理限制广告跟踪
AdvertisingIdClient.getAdvertisingIdInfo() 方法返回一个包含两个关键属性的 AdvertisingIdClient.Info 对象:
getId()(String):代表广告标识符的字母数字字符串。isLimitAdTrackingEnabled()(Boolean):当启用限制广告跟踪时,Google Play 政策禁止使用广告标识符数据来创建广告资料档案或定位个性化广告。该政策仍然允许上下文广告、频次控制、转化跟踪、报告和安全/反欺诈检测等活动;然而,在当前的 Google Play 服务中,启用此设置后,广告标识符本身会被清零,因此应用程序不应假设可用的 GAID 仍然存在。
处理全零标识符和用户发起的删除状态
字符串 00000000-0000-0000-0000-000000000000:何时以及为何出现
在生产环境中,当出现以下情况时,AdvertisingIdClient.Info.getId() 会返回 00000000-0000-0000-0000-000000000000:
- 用户删除了广告标识符:用户导航到设备设置并删除了其广告标识符。
- 启用了限制广告跟踪:用户在受支持的操作系统版本上启用了限制广告跟踪。
- 缺少清单权限:应用程序目标定位为 Android 13+(API 级别 33+),但未在其合并的清单中声明
com.google.android.gms.permission.AD_ID。

用户设置工作流:删除广告标识符
当前 Android 版本上的用户可以通过导航至以下路径来移除其标识符:
设置 > 隐私 > 广告 > 删除广告标识符(具体的标签可能因设备 OEM 而异)。
删除后,Google Play 服务会清除底层数值。所有已安装应用随后的 API 调用都会返回全零字符串。
重置和删除状态的 Google Play 政策要求
根据 Google Play 开发者关于广告标识符的政策,开发人员必须遵循明确的规则处理用户隐私操作:
- 重置要求:当用户重置其广告标识符时,如果没有获得用户的明确同意,开发人员不得将新的广告标识符与先前的广告标识符或衍生自该标识符的数据进行桥接或关联。
- 删除要求:当用户删除其广告标识符时,开发人员不得将该设备与链接至或衍生自其先前广告标识符的数据进行关联。
清理下游数据采集管道中的标识符输入
后端归因引擎必须实施验证过滤器,以防止全零字符串污染归因数据库:
fun isGaidValid(gaid: String?): Boolean {
if (gaid.isNullOrBlank()) return false
val zeroedGaid = "00000000-0000-0000-0000-000000000000"
return !gaid.equals(zeroedGaid, ignoreCase = true)
}
如果检测到全零标识符,下游系统不得将其视为可用的 GAID 或标识符匹配键;任何归因或统计处理都应仅依赖其他单独允许的信号。
Google Play 家庭政策与应用类别限制
家庭应用中的数据实践
Google Play 针对参与家庭计划或面向儿童的应用实施了严格的数据保护规则:
- 禁止传输 AAID:专为儿童设计的应用不得传输广告标识符 (AAID)。对于目标定位为 API 级别 33 或更高版本的应用,Google 建议省略或禁用
AD_ID权限作为一项直接的合规机制。 - 混合受众处理:同时面向儿童和年长受众的应用不得从儿童或年龄未知的使用者传输 AAID。默认情况下,应用和 SDK 应避免传输 AAID,直到年龄验证确认使用者不是儿童。
在清单中使用 tools:node=“remove”
如果您的应用程序面向儿童,但导入的第三方 SDK 通过清单合并自动注入了 AD_ID 权限,请在您的主 AndroidManifest.xml 中添加 tools:node="remove" 属性:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.familyapp">
<!-- 显式移除由传递依赖项注入的 AD_ID -->
<uses-permission
android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove"/>
</manifest>
当 GAID 不可用时的特定用例衡量替代方案
当 GAID 被清零、删除或受政策限制时,工程团队会根据其功能目标部署特定用例的替代方案:
- 营销活动归因:使用 Google Play Install Referrer API 检索通过 Play 商店 URL(
referrer=...)传递的商店中介营销活动参数。该 API 独立于 GAID 删除运行。 - 引导与用户意图:使用第一方上下文参数路由来恢复从网页落地页传递的引荐令牌、促销代码和深度链接目标。
- 开发者作用域的数据统计和反欺诈:使用应用集 ID(
AppSetIdClient)在以同一开发者账号发布的各个应用之间生成共享标识符,而不进行跨应用广告跟踪。
GAID 不可用或为全零
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
付费营销活动归因 引导与引荐意图 开发者自有的
数据统计与反欺诈
│ │ │
▼ ▼ ▼
Google Play 第一方上下文 应用集 ID
Install Referrer 路由 (开发者或
(商店 URL) 应用作用域)

对比矩阵:GAID 与 Install Referrer 与应用集 ID
| 维度 | Google 广告标识符 (GAID) | Google Play Install Referrer | 应用集 ID (AppSet) |
|---|---|---|---|
| 标识符类型 | 可重置的跨应用广告 UUID | 商店中介营销活动参数字符串 | 开发者或应用作用域的标识符,具体取决于安装和 Play 服务上下文 |
| 清单权限 | com.google.android.gms.permission.AD_ID |
无 | 无 |
| 用户删除影响 | 返回一串零 | 独立于 GAID 删除 | 在没有 API 访问权限满 13 个月、集合中的最后一个应用被卸载或恢复出厂设置后可能会重置 |
| 主要用例 | 跨应用广告归因与重定向 | Play 商店营销活动效果跟踪 | 反欺诈检测与开发者自有的数据统计 |
| 家庭政策契合度 | 不得从儿童传输 | 不受特定于 AAID 的传输禁令限制;使用仍必须遵守适用的家庭政策 | Google 鼓励用于家庭应用中的数据统计 |
常见问题解答 (FAQ)
为什么调用 AdvertisingIdClient 会因 IllegalStateException 而崩溃?
如果应用目标定位为 Android 13 且未声明 AD_ID 权限会发生什么?
如何移除由第三方 SDK 依赖项注入的 AD_ID 权限?
总结与决策框架
Google 广告标识符仍然是 Android 广告和使用者数据统计用例的重要可重置标识符,但在 Android 13+ 上运行需要遵守清单权限政策、后台执行标准以及对全零标识符状态的优雅处理。当 GAID 不可用时,工程团队应根据特定用例选择衡量基元——例如,使用 Install Referrer 进行 Play Store 营销活动归因,使用第一方上下文参数处理引导状态,或使用应用集 ID 进行开发者自有的数据统计和反欺诈场景。
要探索 Android 归因和深度链接管道的实现模式,请查阅 OpoInstall 文档。
相关材料
-
概念:广告标识符生命周期、AD_ID 清单权限、后台线程提取、全零标识符处理
-
技术:Google Play 服务、AdvertisingIdClient API、Google Play Install Referrer API、OpoInstall Android SDK
-
标准:Google Play 开发者政策、Android 13 行为变更、IETF RFC 4122 UUID 标准
-
API 与类:
AdvertisingIdClient、AdvertisingIdClient.Info、AppSetIdClient、OpoInstall 上下文 API
官方文档
Share this article



