You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用新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.readonly
    • https://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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 18:35:38