Topics API 开发者指南

在阅读 Privacy Sandbox on Android 文档时,请使用开发者预览版Beta 版按钮选择您所使用的程序版本,因为不同版本的说明可能会有所不同。


提供反馈

Topics API 可根据用户的应用使用情况来粗略推断设备上的兴趣信号。这些信号(称为“主题”)会与广告主共享,以支持针对用户兴趣投放广告,而无需跨应用跟踪单个用户。设计方案中详细了解 Topics API。

重要提示:点击“SDK 扩展版本”或“开发者预览版”按钮可选择您所使用的程序版本,因为说明可能会有所不同。

设置

使用最新的 Android Privacy Sandbox SDK,以获取最新版本的可保护隐私的 API。您需要在清单中添加权限并创建广告服务配置,以便您的应用使用 Topics API:

<uses-permission android:name="android.permission.ACCESS_ADSERVICES_TOPICS" />

在清单的 <application> 元素中引用广告服务配置:

<property android:name="android.adservices.AD_SERVICES_CONFIG"
   android:resource="@xml/ad_services_config" />

指定清单中引用的广告服务 XML 资源,例如 res/xml/ad_services_config.xml。您可以使用 allowAllToAccess 属性授予对所有 SDK 的访问权限,也可以使用 allowSdksToAccess 属性授予对各个 SDK 的访问权限:详细了解广告服务权限和 SDK 访问权限控制

<ad-services-config>
    <topics allowAllToAccess="true" />
</ad-services-config>

此外,您还必须使用这些 adb 命令启用对 Topics API 的访问权限(默认处于停用状态)。

adb shell device_config put adservices ppapi_app_signature_allow_list \"*\"
adb shell setprop debug.adservices.disable_topics_enrollment_check true

Topics API 的主要功能位于 TopicsManager 对象内的 getTopics() 方法中,如以下示例所示:

Kotlin

fun getTopics(
        getTopicsRequest: GetTopicsRequest,
        executor: Executor,
        callback: OutcomeReceiver<GetTopicsResponse, Exception>
    ) { }

Java

public void getTopics (@NonNull GetTopicsRequest getTopicsRequest,
    @NonNull Executor executor,
    @NonNull OutcomeReceiver<GetTopicsResponse, Exception> callback)

如需使用此方法,请初始化 TopicsManager 对象以及接收主题数据所需的参数。GetTopicsRequest 用于传递所需信息以检索 Topics API 数据,其中包括一个用于指示调用方是否作为观察者的标志。作为观察者时,getTopics 调用将返回上一个周期的主题,但不会影响下一个周期的主题数据。OutcomeReceiver 回调会异步处理结果。例如:

Kotlin

private fun topicGetter() {
    val mContext = baseContext
    val mTopicsManager = mContext.getSystemService(TopicsManager::class.java)
    val mExecutor: Executor = Executors.newCachedThreadPool()
    val shouldRecordObservation = false
    val mTopicsRequestBuilder: GetTopicsRequest.Builder = GetTopicsRequest.Builder()
    mTopicsRequestBuilder.setAdsSdkName(baseContext.packageName)
    mTopicsRequestBuilder.setShouldRecordObservation(shouldRecordObservation)
    mTopicsManager.getTopics(mTopicsRequestBuilder.build(), mExecutor,
        mCallback as OutcomeReceiver<GetTopicsResponse, Exception>)
}

private var mCallback: OutcomeReceiver<GetTopicsResponse, java.lang.Exception> =
object : OutcomeReceiver<GetTopicsResponse, java.lang.Exception> {
    override fun onResult(result: GetTopicsResponse) {
        // handle successful result
        val topicsResult = result.topics
        for (i in topicsResult.indices) {
            Log.i("Topic", topicsResult[i].getTopicId().toString())
        }
        if (topicsResult.size == 0) {
            Log.i("Topic", "Returned Empty")
        }
    }

    override fun onError(error: java.lang.Exception) {
        // handle error
        Log.i("Topic", "Error, did not return successfully")
    }
}

Java

public void TopicGetter() {
    @NonNull Context mContext = getBaseContext();
    TopicsManager mTopicsManager = mContext.getSystemService(TopicsManager.class);
    Executor mExecutor = Executors.newCachedThreadPool();
    boolean shouldRecordObservation = false;
    GetTopicsRequest.Builder mTopicsRequestBuilder = new GetTopicsRequest.Builder();
    mTopicsRequestBuilder.setAdsSdkName(getBaseContext().getPackageName());
    mTopicsRequestBuilder.setShouldRecordObservation(shouldRecordObservation);
    mTopicsManager.getTopics(mTopicsRequestBuilder.build(), mExecutor, mCallback);
}

OutcomeReceiver mCallback = new OutcomeReceiver<GetTopicsResponse, Exception>() {
    @Override
    public void onResult(@NonNull GetTopicsResponse result) {
        //Handle Successful Result
        List<Topic> topicsResult = result.getTopics();
        for (int i = 0; i < topicsResult.size(); i++) {
            Log.i("Topic", topicsResult.get(i).getTopicId().toString());
        }
        if (topicsResult.size() == 0) {
            Log.i("Topic", "Returned Empty");
        }
    }

    @Override
    public void onError(@NonNull Exception error) {
        // Handle error
        Log.i("Topic", "Experienced an error, and did not return successfully");

    }
};

请求一组主题

设置就绪后,您就可以调用 getTopics() 方法来接收 GetTopicsResponse 结果:

Kotlin

mTopicsManager.getTopics(mTopicsRequestBuilder.build(), mExecutor,
        mCallback as OutcomeReceiver<GetTopicsResponse, java.lang.Exception>)

Java

mTopicsManager.getTopics(mTopicsRequestBuilder.build(), mExecutor, mCallback);

上述调用会返回与用户相关的主题对象列表(其中包含与开源分类中的主题相对应的 ID 值)或相关错误。这些主题对象列表将类似于以下示例:

/Internet & Telecom/Text & Instant Messaging

如需查看可能返回的主题列表,请参阅分类。此分类是开源的,您可以使用此页面顶部的反馈按钮提交建议的更改。

测试

Topics API 会根据应用使用情况提供最新的相关主题。此早期版本可预览 API 行为,我们会在将来的版本中提高主题的质量。

为了获得最充分的体验,建议您使用含有多个应用的测试环境,在该环境中调用 getTopics() 来查看如何选择主题。为帮助您开始使用,GitHub 上的 SDK 运行时和隐私保护 API 代码库中提供了一组独立的 Android Studio 项目,其中包括展示如何初始化和调用 Topics API 的示例。

主题计算会在“周期”结束时进行。默认情况下,每个周期为 7 天,但您可以修改获取结果的这一时间间隔。以下 Android 调试桥 shell 命令可以将周期长度缩短为 5 分钟:

adb shell device_config put adservices topics_epoch_job_period_ms 30000

您可以使用 getprop 确认 topics_epoch_job_period_ms 值:

adb shell device_config get adservices topics_epoch_job_period_ms

如需手动触发周期计算,请执行以下命令:

adb shell cmd jobscheduler run -f com.google.android.adservices.api 2

除了使用示例应用之外,您还可以使用 Colab 针对主题分类器测试不同的应用信息组合。使用此 Colab 查看您的应用在调用 getTopics 时可能会获得的结果类型。

限制

有关 Topics API 的正在开发中的功能列表,请查看版本说明

报告 bug 和问题

您的反馈对 Privacy Sandbox on Android 至关重要!如果发现任何问题或有任何关于改进 Privacy Sandbox on Android 的想法,欢迎告诉我们。