Android AdMob(三) — GDPR相关处理

GDPR(General Data Protection Regulation)是欧盟针对数据保护和隐私权的一项法规。旨在加强个人数据保护,并为欧盟居民提供更多对其个人数据的控制权,该法规在2018年5月25日生效。

从2024年1月16日开始,使用AdManager或AdMob的发布者和开发者在向欧盟或英国的用户提供广告前,必须先使用由Google认证并集成了IAB's Transparency and Consent Framework的同意管理平台(CMP)获取用户授权。在获得授权后,才能使用用户信息进行个性化广告投放。

相关条例

User Messaging Platform(UMP)

针对新的限制条例,Google提供了User Messaging Platform(UMP)SDK,便于开发者在App中集成,实现获取用户同意功能。

UMP官方接入文档

在AdMob管理后台创建GDPR消息

官方指引文档

  • 在AdMob管理后台的隐私权与消息中选择创建GDPR信息。

  • 在GDPR消息创建页面中,首先需要选取使用该消息的App。需要注意的是,这里需要填入App使用的隐私政策对应的网址。

  • 根据需求调整弹窗样式以及此消息弹窗面向的人群后就可以发布。

添加UMP SDK

在项目app module的build.gradle中的dependencies中添加依赖:

scss 复制代码
dependencies {
    implementation("com.google.android.ump:user-messaging-platform:2.1.0")
}

配置AndroidManifest

设置APPLICATION_ID

AndroidManifest中的com.google.android.gms.ads.APPLICATION_ID配置为关联了GDPR消息的APPLICATION_ID。代码如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        ......
        >

        <!--此id为官方demo的id,可以测试UMP效果-->
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-3940256099942544~3347511713" />

    </application>
</manifest>

延迟应用衡量

AdMob会在应用启动时初始化应用衡量并开始上报用户级事件,可以通过在AndroidManifest中配置<meta-data>延迟此行为。代码如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        ......
        >

        <meta-data
            android:name="com.google.android.gms.ads.DELAY_APP_MEASUREMENT_INIT"
            android:value="true" />

    </application>
</manifest>

实现获取用户同意功能

请求同意信息

创建ConsentInformation对象然后通过requestConsentInfoUpdate()方法请求同意信息,代码如下:

kotlin 复制代码
class AdmobExampleActivity : AppCompatActivity() {

    private lateinit var binding: LayoutAdmobExampleActivityBinding

    private lateinit var consentInformation: ConsentInformation

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = LayoutAdmobExampleActivityBinding.inflate(layoutInflater).also {
            setContentView(it.root)
        }
        binding.includeTitle.tvTitle.text = "AdmobExample"

        val consentRequestParameters = ConsentRequestParameters.Builder()
            // 设置用户不会低于法定年龄
            .setTagForUnderAgeOfConsent(false)
            .build()
        consentInformation = UserMessagingPlatform.getConsentInformation(this)
        consentInformation.requestConsentInfoUpdate(
            this,
            consentRequestParameters,
            object : ConsentInformation.OnConsentInfoUpdateSuccessListener {
                override fun onConsentInfoUpdateSuccess() {
                    // 同意信息更新成功

                    if (consentInformation.isConsentFormAvailable) {
                        // 同意表单可用,加载并显示表单
                    } else {
                        // 同意表单不可用,验证一下是否可以使用广告
                        if (consentInformation.canRequestAds()) {
                            // 可以请求广告,开始初始化广告SDK
                        }
                    }
                }
            },
            object : ConsentInformation.OnConsentInfoUpdateFailureListener {
                override fun onConsentInfoUpdateFailure(formError: FormError) {
                    // 同意信息更新失败

                    // 获取错误码
                    val errorCode = formError.errorCode
                    // 获取错误信息
                    val errorMessage = formError.message

                    // 验证一下是否可以使用广告
                    if (consentInformation.canRequestAds()) {
                        // 可以请求广告,开始初始化广告SDK
                    }
                }
            })
    }
}

获取同意信息可能出现几种情况,需要做出对应的处理,如下:

  • 更新信息成功且同意表单可用,此时应该加载并显示表单。
  • 更新信息成功但表单不可用,此时应该通过canRequestAds()检测是否可以请求广告。
  • 更新信息失败,此时也应该通过canRequestAds()检测是否可以请求广告。

另外,需要注意的是,为了符合IAB TCF的标准,官方建议在每次运行App时都执行requestConsentInfoUpdate()方法,避免用户上一次授权的同意信息已经超过13个月导致失效。

加载同意表单并显示。

在成功更新完同意信息后,如果同意表单可用,那么此时应该加载同意表单并显示。代码如下:

kotlin 复制代码
class AdmobExampleActivity : AppCompatActivity() {

    private lateinit var binding: LayoutAdmobExampleActivityBinding

    private lateinit var consentInformation: ConsentInformation

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = LayoutAdmobExampleActivityBinding.inflate(layoutInflater).also {
            setContentView(it.root)
        }
        binding.includeTitle.tvTitle.text = "AdmobExample"

        val consentRequestParameters = ConsentRequestParameters.Builder()
            .setTagForUnderAgeOfConsent(false)
            .build()
        consentInformation = UserMessagingPlatform.getConsentInformation(this)
        consentInformation.requestConsentInfoUpdate(
            this,
            consentRequestParameters,
            object : ConsentInformation.OnConsentInfoUpdateSuccessListener {
                override fun onConsentInfoUpdateSuccess() {
                    if (consentInformation.isConsentFormAvailable) {
                        loadAndShowConsentFormIfRequired()
                    } 
                    ......
                }
            },
            object : ConsentInformation.OnConsentInfoUpdateFailureListener {
                override fun onConsentInfoUpdateFailure(formError: FormError) {
                    ......
                }
            })
    }
    
    fun loadAndShowConsentFormIfRequired() {
        UserMessagingPlatform.loadAndShowConsentFormIfRequired(this, object : ConsentForm.OnConsentFormDismissedListener {
            override fun onConsentFormDismissed(formError: FormError?) {
                // 表单加载失败时回调此方法。
                // 表单加载完并显示成功,用户操作完表单后回调此方法。
                // 验证一下是否可以使用广告
                if (consentInformation.canRequestAds()) {
                    // 可以请求广告,开始初始化广告SDK
                }
            }
        })
    }
}

onConsentFormDismissed()会在表单加载失败或者表单被用户关闭之后(不论用户选择同意或不同意)回调。此时同样应该通过canRequestAds()检测是否可以请求广告。

允许撤销授权

根据GDPR法规,用户可以随时撤销同意授权,因此需要在App中提供撤销授权功能。UMP为此提供了Privacy Options API,在requestConsentInfoUpdate()回调成功后,可以通过ConsentInformationgetPrivacyOptionsRequirementStatus()方法获取是否需要使用Privacy Options。如果需要的话,通过UserMessagingPlatformshowPrivacyOptionsForm()方法显示Privacy Options。代码如下:

kotlin 复制代码
class AdmobExampleActivity : AppCompatActivity() {

    private lateinit var binding: LayoutAdmobExampleActivityBinding

    private lateinit var consentInformation: ConsentInformation

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = LayoutAdmobExampleActivityBinding.inflate(layoutInflater).also {
            setContentView(it.root)
        }
        binding.includeTitle.tvTitle.text = "AdmobExample"
        binding.btnShowPrivacyOptions.setOnClickListener { showPrivacyOptionsForm() }

        val consentRequestParameters = ConsentRequestParameters.Builder()
            .setTagForUnderAgeOfConsent(false)
            .build()
        consentInformation = UserMessagingPlatform.getConsentInformation(this)
        consentInformation.requestConsentInfoUpdate(
            this,
            consentRequestParameters,
            object : ConsentInformation.OnConsentInfoUpdateSuccessListener {
                override fun onConsentInfoUpdateSuccess() {
                    if (consentInformation.privacyOptionsRequirementStatus == ConsentInformation.PrivacyOptionsRequirementStatus.REQUIRED) {
                        // 需要显示Privacy Options,显示对应的按钮
                        binding.btnShowPrivacyOptions.visibility = View.VISIBLE
                    }
                    ......
                }
            },
            object : ConsentInformation.OnConsentInfoUpdateFailureListener {
                override fun onConsentInfoUpdateFailure(formError: FormError) {
                    ......
                }
            })
    }

    private fun showPrivacyOptionsForm() {
        UserMessagingPlatform.showPrivacyOptionsForm(this, object : ConsentForm.OnConsentFormDismissedListener {
            override fun onConsentFormDismissed(formError: FormError?) {
                // 表单加载失败时回调此方法。
                // 表单加载完并显示成功,用户操作完表单后回调此方法。
            }
        })
    }
}

测试

UMP提供了一些测试方法,可以模拟App在欧盟或英国区域时的情况和快速撤销同意授权,使用步骤如下:

  1. 获取设备的测试ID。

调用requestConsentInfoUpdate()方法后,在Logcat中过滤tag为UserMessagingPlatform的日志,如下:

  1. 调整ConsentRequestParameters配置。

在创建ConsentRequestParameters时设置获取到的TestDeviceHashedId以及地理位置信息,代码如下:

kotlin 复制代码
class AdmobExampleActivity : AppCompatActivity() {

    private lateinit var consentInformation: ConsentInformation

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val consentRequestParameters = ConsentRequestParameters.Builder()
            .setTagForUnderAgeOfConsent(false)
            .setConsentDebugSettings(ConsentDebugSettings.Builder(this)
                .setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA)
                .addTestDeviceHashedId("9E190127A33E1387D56E000EEF479712")
                .build())
            .build()
        consentInformation.requestConsentInfoUpdate(
            this,
            consentRequestParameters,
            object : ConsentInformation.OnConsentInfoUpdateSuccessListener {
                override fun onConsentInfoUpdateSuccess() {
                    ......
                }
            },
            object : ConsentInformation.OnConsentInfoUpdateFailureListener {
                override fun onConsentInfoUpdateFailure(formError: FormError) {
                    ......
                }
            })
    }
}
  1. 在需要时撤销同意授权,代码如下:
kotlin 复制代码
class AdmobExampleActivity : AppCompatActivity() {

    private lateinit var consentInformation: ConsentInformation

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        consentInformation = UserMessagingPlatform.getConsentInformation(this)
        consentInformation.reset()
    }
}

效果演示与示例代码

最终效果如下图:

演示代码已在示例Demo中添加。

ExampleDemo github

ExampleDemo gitee

相关推荐
2501_929157682 小时前
Switch 20.5.0系统最新PSP模拟器懒人包
android·游戏·ios·pdf
用户093 小时前
Kotlin Flow的6个必知高阶技巧
android·面试·kotlin
用户093 小时前
Flutter插件与包的本质差异
android·flutter·面试
用户094 小时前
Jetpack Compose静态与动态CompositionLocal深度解析
android·面试·kotlin
聆风吟º6 小时前
【Spring Boot 报错已解决】别让端口配置卡壳!Spring Boot “Binding to target failed” 报错解决思路
android·java·spring boot
非专业程序员Ping14 小时前
HarfBuzz概览
android·ios·swift·font
Jeled15 小时前
「高级 Android 架构师成长路线」的第 1 阶段 —— 强化体系与架构思维(Clean Architecture 实战)
android·kotlin·android studio·1024程序员节
明道源码17 小时前
Kotlin 控制流、函数、Lambda、高阶函数
android·开发语言·kotlin
消失的旧时光-194319 小时前
Kotlin × Gson:为什么遍历 JsonObject 要用 entrySet()
android·kotlin·数据处理·1024程序员节
G果20 小时前
安卓APP页面之间传参(Android studio 开发)
android·java·android studio