在 Android 上實作 Topics API

設定

如要導入 Topics API,您必須先設定開發環境。執行以下設定步驟:

  1. 請使用最新的 Android Privacy Sandbox SDK 取得最新版本 新版隱私權保護 API 的版本

  2. 將以下內容新增到資訊清單:

    • 權限:加入 ACCESS_ADSERVICES_TOPICS 權限,讓應用程式存取 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>
      
  3. 使用 Privacy Sandbox 註冊廣告技術,在 SDK 中呼叫 Topics API。如要在本機進行測試,您可以使用下列指令停用 Topics 註冊檢查:

    adb shell setprop debug.adservices.disable_topics_enrollment_check true
    
  4. 啟用 Topics API 存取權。Topics API 預設為停用。您需要使用 ADB 指令啟用這個功能:

    adb shell device_config put adservices ppapi_app_signature_allow_list \"\*\"
    adb shell setprop debug.adservices.disable_topics_enrollment_check true
  5. 建立並執行範例應用程式的 JavaKotlin 版本程式碼,以熟悉在裝置上擷取主題的方式。

要求一組主題

Topics API 的主要功能位於 TopicsManager 物件中的 getTopics() 方法中,如以下範例所示:

Kotlin Java
fun getTopics(
        getTopicsRequest: GetTopicsRequest,
        executor: Executor,
        callback: OutcomeReceiver<GetTopicsResponse, Exception>
    ) { }
public void getTopics (@NonNull GetTopicsRequest getTopicsRequest,
    @NonNull Executor executor,
    @NonNull OutcomeReceiver<GetTopicsResponse, Exception> callback)

如要使用這個方法,請初始化 TopicsManager 物件,以及接收主題資料所必需的參數。GetTopicsRequest 會傳送必要資訊來擷取 Topics API 資料,包括用來表示呼叫端是否做為觀測器的旗標。做為觀測器時,getTopics 呼叫會傳回上個週期的主題,但不會影響下一個週期的主題資料。 OutcomeReceiver 回呼會以非同步的方式處理結果。例如:

Kotlin Java
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")
    }
}
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");
    }
};

準備就緒後,你可以撥打電話以便接收 GetTopicsResponse 產生的結果為 getTopics() 方法:

Kotlin Java
mTopicsManager.getTopics(mTopicsRequestBuilder.build(), mExecutor,
        mCallback as OutcomeReceiver<GetTopicsResponse, java.lang.Exception>)
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 後可能取得的結果類型。

加密詳細資料

隨著加密功能的推出,對 GetTopics() 的呼叫現在會產生包含 EncryptedTopic 物件清單的回應。解密這些結果之後 會產生與前一個 Topic 物件相同 JSON 格式的物件。

Topics API 支援單樣本導入 HPKE (混合型公開金鑰) 加密)。我們預期已註冊的呼叫端在 註冊期間提供的公開加密網址端點。這些金鑰應採用 Base64 編碼。

EncryptedTopic 物件有 3 個欄位。傳回的主題清單可以 透過該公開金鑰的對應私密金鑰取得。

如果是開發用途,您可以停用 Topics API 加密功能,藉此測試 檢查狀態這會強制 API 使用測試公開金鑰來加密回應。您可以使用 對應的私密金鑰

限制

如需 Topics API 處於開發階段的功能清單,請參閱「版本資訊」。

回報錯誤和問題

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

後續步驟

瞭解使用者和開發人員如何管理及自訂 Topics API,以符合使用者的偏好設定和需求。
瞭解 Topics 在 Android 上的運作方式,並掌握 API 流程的核心步驟。

另請參閱

請參閱我們的資源,進一步瞭解 Android 上的 Topics API。