如何使用新Credential Manager实现Google登录并获取Admob统计数据
基于Credential Manager的Admob API授权实现方案
一、基础配置
1. 依赖引入
在Android项目的build.gradle(Module级别)中添加Credential Manager和Google Identity相关依赖:
dependencies { // Credential Manager核心库 implementation "androidx.credentials:credentials:1.2.0" implementation "androidx.credentials:credentials-play-services-auth:1.2.0" // Google ID授权支持 implementation "com.google.android.libraries.identity.googleid:googleid:1.1.0" }
2. OAuth客户端配置
- 登录Google Cloud Console,创建Web类型的OAuth 2.0客户端ID(需关联你的Android应用包名和SHA-1指纹)
- 在OAuth consent screen中添加以下权限范围:
https://www.googleapis.com/auth/admob.readonlyhttps://www.googleapis.com/auth/admob.report- 若需长期刷新令牌,额外添加
https://www.googleapis.com/auth/offline
二、授权流程实现
1. 初始化Credential Manager
在Activity/Fragment中创建实例:
val credentialManager = CredentialManager.create(this)
2. 构建授权请求
配置Google ID授权选项,指定客户端ID、权限范围等参数:
private fun buildGoogleIdRequest(): GetCredentialRequest { val googleIdOption = GetGoogleIdOption.Builder() .setServerClientId("YOUR_WEB_CLIENT_ID") // 替换为你的Web客户端ID .setFilterByAuthorizedAccounts(false) // 允许用户选择新账号 .setNonce(generateRandomNonce()) // 生成随机字符串防重放攻击 .setScopes( listOf( "https://www.googleapis.com/auth/admob.readonly", "https://www.googleapis.com/auth/admob.report", "https://www.googleapis.com/auth/offline" // 可选,用于获取刷新令牌 ) ) .build() return GetCredentialRequest.Builder() .addCredentialOption(googleIdOption) .build() } // 生成随机Nonce的工具方法 private fun generateRandomNonce(): String { val bytes = ByteArray(16) SecureRandom().nextBytes(bytes) return Base64.encodeToString(bytes, Base64.NO_WRAP) }
3. 发起授权请求并处理结果
使用Activity Result Launcher处理授权回调:
// 注册Activity Result Launcher private val getCredentialLauncher = registerForActivityResult(GetCredentialContract()) { result -> when (result) { is GetCredentialResponse -> handleCredentialSuccess(result.credential) is GetCredentialException -> handleCredentialError(result) } } // 处理授权成功 private fun handleCredentialSuccess(credential: Credential) { if (credential is GoogleIdTokenCredential) { val accessToken = credential.accessToken val refreshToken = credential.refreshToken // 仅首次授权或重新授权时返回 val expiresAt = credential.expirationTimeMillis // 安全存储令牌(建议用EncryptedSharedPreferences) saveTokens(accessToken, refreshToken, expiresAt) // 调用Admob API获取统计数据 fetchAdmobStatistics(accessToken) } } // 处理授权错误 private fun handleCredentialError(exception: GetCredentialException) { when (exception.type) { GetCredentialException.TYPE_USER_CANCELED -> { // 用户取消授权,可提示重新尝试 } GetCredentialException.TYPE_NO_CREDENTIALS_AVAILABLE -> { // 无可用账号,引导用户登录 } else -> { // 其他错误,如网络问题、授权失败 Log.e("AuthError", exception.message ?: "Unknown error") } } } // 触发授权请求 fun startAuthFlow() { getCredentialLauncher.launch(buildGoogleIdRequest()) }
三、调用Admob API
使用获取到的accessToken发起API请求,以获取 mediation 统计数据为例:
private fun fetchAdmobStatistics(accessToken: String) { val okHttpClient = OkHttpClient() val requestBody = """ { "reportSpec": { "dateRange": { "startDate": {"year": 2024, "month": 6, "day": 1}, "endDate": {"year": 2024, "month": 6, "day": 30} }, "metrics": ["ESTIMATED_EARNINGS", "IMPRESSIONS"], "dimensions": ["DATE", "AD_UNIT_NAME"] } } """.trimIndent() val request = Request.Builder() .url("https://admob.googleapis.com/v1/accounts/{YOUR_ADMOB_ACCOUNT_ID}/mediationStats:generate") .addHeader("Authorization", "Bearer $accessToken") .addHeader("Content-Type", "application/json") .post(RequestBody.create(MediaType.parse("application/json"), requestBody)) .build() okHttpClient.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { // 处理网络请求失败 } override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { val responseJson = response.body?.string() // 解析并处理统计数据 } else { when (response.code()) { 401 -> { // 令牌过期,需重新授权或用刷新令牌获取新令牌 refreshAccessToken() } 403 -> { // 权限不足,检查OAuth权限范围配置 } else -> { // 其他API错误 } } } } }) }
四、令牌管理与刷新
1. 令牌存储
使用EncryptedSharedPreferences安全存储访问令牌、刷新令牌和过期时间,避免明文存储。
2. 令牌刷新
当访问令牌过期时,使用刷新令牌获取新的访问令牌:
private fun refreshAccessToken() { val refreshToken = getSavedRefreshToken() ?: return val okHttpClient = OkHttpClient() val requestBody = FormBody.Builder() .add("grant_type", "refresh_token") .add("client_id", "YOUR_WEB_CLIENT_ID") .add("refresh_token", refreshToken) .build() val request = Request.Builder() .url("https://oauth2.googleapis.com/token") .post(requestBody) .build() okHttpClient.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { // 刷新失败,引导用户重新授权 } override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { val json = JSONObject(response.body?.string()) val newAccessToken = json.getString("access_token") val newExpiresIn = json.getLong("expires_in") val newExpiresAt = System.currentTimeMillis() + newExpiresIn * 1000 // 更新存储的令牌 updateAccessToken(newAccessToken, newExpiresAt) // 重新调用Admob API fetchAdmobStatistics(newAccessToken) } else { // 刷新令牌无效,引导用户重新授权 } } }) }
五、关键注意事项
- Web客户端ID的必要性:Android客户端ID无法直接获取访问令牌,必须使用Web类型客户端ID并关联Android应用
- OAuth consent screen发布:若应用面向外部用户,需将OAuth consent screen发布为"Production"状态,否则仅测试账号可授权
- 权限最小化:仅请求必要的权限范围,减少用户拒绝授权的概率
- 错误处理:重点处理令牌过期、权限不足、用户取消等常见场景,提升用户体验
内容的提问来源于stack exchange,提问作者Anshuman M.
相关产品推荐
相关产品推荐

