Topics API 開發人員指南

詳閱 Android 版 Privacy Sandbox 說明文件時,請利用「開發人員預覽版」或「Beta 版」按鈕選取您要使用的計畫版本,因為兩者的操作說明可能不盡相同。


提供意見

Topics API 會根據使用者的應用程式使用情形,在裝置上推斷出概略的興趣信號。這些稱為「主題」的信號會提供給廣告主,讓他們無需跨多個應用程式追蹤個別使用者,就能依據使用者的興趣投放廣告。如要進一步瞭解 Topics API,請參閱設計提案重要事項:選取「SDK Extensions Release」或「Developer Preview」按鈕,選擇您要使用的計畫版本,因為兩者的操作說明可能不盡相同。

設定

請使用最新的 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 值的 Topics 物件清單,這些值會對應至開放原始碼分類中與使用者相關的主題,或對應至相關的錯誤。主題會類似於下列範例:

/Internet & Telecom/Text & Instant Messaging

如需可能傳回的主題清單,請參閱分類相關資訊。這套分類是開放原始碼,因此您可以使用本頁頂端的意見回饋按鈕提出建議,指出需要的變更。

測試

Topics API 會根據應用程式的使用情況提供相關的最新主題。這個早期版本可讓您預覽 API 的行為,我們會在日後版本中不斷改進主題的品質。為獲得完整體驗,建議使用包含多個應用程式的測試環境,並可在其中呼叫 getTopics() 瞭解主題的選用情形。GitHub 上的 SDK 執行階段和隱私權保護 API 存放區包含一組可幫助您入門的 Android Studio 專案,其中包括一些展示如何初始化和呼叫 Topics API 的範例。

「週期」結束時會計算主題。在預設情況下,每個週期都是 7 天,但您可以修改時間間隔,以便快速取得結果。此 Android Debug Bridge 殼層指令會將週期的長度縮短為 5 分鐘:

adb shell device_config put adservices topics_epoch_job_period_ms 300000

您可以使用 get 確認 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 處於開發階段的功能清單,請參閱「版本資訊」。

回報錯誤和問題

您的意見回饋對 Android 版 Privacy Sandbox 至關重要!如果您發現任何問題,或希望對 Android 版 Privacy Sandbox 提出改進意見,請告訴我們