SDK 執行階段的回溯相容性

本文件介紹新的 Jetpack 程式庫,可協助開發人員遷移至 SDK 執行階段。此外,也會說明 SDK 執行階段如何支援先前的 Android 平台版本 (從建構到執行),以及開發人員可以預期的執行階段環境差異或限制。開發人員可使用這個程式庫為應用程式或 SDK 建立單一版本,且不管裝置是否支援 SDK 執行階段,都能順利執行版本。

此程式庫是透過下列元件達到回溯相容性:

  • Android Gradle 外掛程式 (AGP) + Bundletool 會將 SDK 執行階段封裝至 APK,建構可在不支援 SDK 執行階段的裝置上使用的應用程式變化版本。

  • SDK 執行階段用戶端程式庫 (androidx.privacysandbox.sdkruntime:sdkruntime-client) 會從應用程式素材資源載入封裝的 SDK,並在不支援 SDK 執行階段的裝置中模擬 SDK 執行階段。

  • SDK 執行階段提供者程式庫 (androidx.privacysandbox.sdkruntime:sdkruntime-provider) 會為 SDK 提供一個可從 SDK 執行階段用戶端程式庫載入的 API。

透過 Bundletool 提交 SDK

在支援 SDK 執行階段的裝置中,系統以獨立套件的形式提供並安裝 SDK。

為了對不支援 SDK 執行階段的平台版本提供支援,Bundletool 會建構一或多個應用程式 APK 組合的變化版本,其中包含應用程式所需的所有 SDK。每個 SDK 都會封裝成獨立的 APK 分割。此外,系統會執行以下轉換作業:

  1. 將 SDK 位元碼 (DEX) 檔案以素材資源的形式複製到 SDK 分割中。
  2. 將 SDK Java 資源以素材資源的形式複製到 SDK 分割中。
  3. 重新對應 SDK 資源,並與應用程式資源合併。
  4. 產生 SDK 執行階段用戶端程式庫的設定。

使用 SDK 執行階段用戶端程式庫載入 SDK

SDK 執行階段用戶端程式庫提供的 API 與平台 API 類似,但支援 SDK 執行階段環境中的 SDK,以及封裝至變化版本應用程式的 SDK。

如要使用 SDK 執行階段用戶端程式庫,請新增依附元件 androidx.privacysandbox.sdkruntime:sdkruntime-client 並使用 SdkSandboxManagerCompat,不要使用 SdkSandboxManager

當應用程式嘗試載入 SDK 時,程式庫會先檢查 SDK 在建構期間是否封裝至應用程式。如果 SDK 已封裝,程式庫會從 SDK 分割中擷取 SDK,並載入到應用程式處理程序。如果 SDK 未封裝至應用程式,程式庫會委派平台 API 載入 SDK。

從素材資源中擷取 SDK

當應用程式嘗試載入封裝的 SDK 時,SDK 執行階段用戶端程式庫會檢查 SDK 的 DEX 檔案,確認其是否已擷取至裝置儲存空間 (code_cache),如果沒有,就會從素材資源中擷取檔案。

通常在應用程式安裝或更新完成後,程式庫才會擷取檔案一次。

如果可用儲存空間低於允許的門檻 (目前為 100 MB),而且未擷取任何 DEX 檔案,程式庫會嘗試直接從支援裝置 (API 27 以上版本) 上的素材資源載入 SDK,導致記憶體用量較高。

SDK 類別的類別載入器

為避免 SDK 和應用程式類別之間發生衝突,系統會使用個別的類別載入器 (完全獨立於主要應用程式類別載入器) 載入所有 SDK 類別。

在目前的 SDK 執行階段設計中,應用程式和 SDK 之間的所有通訊都會使用 Binder IPC 呼叫。封裝的 SDK 會使用相同的 SDK Binder 物件,而 Binder 交易序列化允許應用程式開發人員將 SDK Binder 物件投放到應用程式端的 SDK Binder 介面。

如果是其他的內部互動 (例如初始化 SDK、提供控制器 API 給 SDK 等),程式庫會使用 Reflection 和 Dynamic Proxy,在不同的類別載入器之間運作。

SDK 環境

SDK 執行階段提供者程式庫會為 SDK 開發人員提供 API。這些 API 與平台 API 類似,但可透過 SDK 執行階段環境和 SDK 執行階段用戶端程式庫載入 SDK。

如要使用程式庫 SDK,您必須新增 androidx.privacysandbox.sdkruntime:sdkruntime-provider 依附元件、擴充 SandboxedSdkProviderCompat (而非 SandboxedSdkProvider),

並且使用 SandboxedSdkProviderAdapter 做為 SDK 提供者,以便在 SDK 執行階段環境中載入相容提供者。

當在 SDK 執行階段中載入 SDK 時,SdkSandboxControllerCompat 會委派給平台 API;而當 SDK 做為封裝的 SDK 載入時,則會委派給 SDK 執行階段用戶端程式庫。

如果是封裝的 SDK,程式庫會修改 SDK 環境,模擬與 SDK 執行階段環境類似的行為。

以下各節將說明 SDK 執行階段用戶端程式庫載入 SDK 時的預期行為。

SDK 資源

在應用程式程序中載入 SDK 時,系統支援 SDK 資源 (res/)。Bundletool 可將所有 SDK 資源與應用程式資源合併。

為了避免衝突,系統會變更所有資源 ID 中的 packageId 前置字串,重新對應 SDK 資源。

當 SDK 執行階段用戶端程式庫載入 SDK 時,系統會在執行階段更新 packageId,允許使用 R 類別處理重新對應的資源。

Java 資源

在應用程式程序中載入 SDK 時,系統支援 Java 資源。Bundletool 會將所有 SDK Java 資源複製到應用程式素材資源中的特殊目錄。SDK 執行階段用戶端程式庫會使用中繼類別載入器,將所有 Java 資源相關呼叫重新導向至新的根目錄。

SDK 素材資源

SDK 素材資源會與應用程式素材資源合併,不會重新對應。

SDK 儲存空間

為支援 SDK 儲存空間,SDK 執行階段用戶端程式庫會為應用程式儲存空間中每個封裝的 SDK,建立專屬根目錄及提供特殊結構,並將此目錄做為儲存空間根目錄。

這個結構可以從 SandboxedSdkProviderCompat#getContext 擷取。

支援的儲存空間相關方法:

  • getDataDir
  • getCacheDir
  • getCodeCacheDir
  • getNoBackupFilesDir
  • getDir
  • getFilesDir
  • openFileInput
  • openFileOutput
  • deleteFile
  • getFileStreamPath
  • fileList
  • getDatabasePath
  • openOrCreateDatabase
  • moveDatabaseFrom - 僅在 SDK 結構之間
  • deleteDatabase
  • databaseList
  • getSharedPreferences
  • moveSharedPreferencesFrom - 僅在 SDK 結構之間
  • deleteSharedPreferences

您可以在該結構中呼叫 createDeviceProtectedStorageContext(),建立保護裝置的儲存空間結構。

SdkSandboxControllerCompat

SDK 執行階段用戶端程式庫可為在應用程式程序中載入的封裝 SDK 提供 SdkSandboxControllerCompat 實作項目。

如果用戶端程式庫不支援 API (例如建構 SDK 的用戶端程式庫版本比應用程式的版本還新),系統會使用最適合的備用方案 (免人工管理或例外狀況)。

版本管理

當 SDKRuntime 用戶端程式庫載入封裝的 SDK 時,會透過 SDK 中的 SDK 執行階段提供者程式庫執行握手。在握手期間,程式庫會交換版本並調整行為,以最合適的備用方案 (免人工管理或例外狀況) 取代未提供的 API。

強烈建議應用程式和 SDK 的開發人員使用最新版程式庫,否則,需要兩者皆支援才可使用的功能可能無法運作。

不管是何種版本的 SDK 執行階段用戶端程式庫,都可以透過任何版本的 SDK 執行階段提供者程式庫載入 SDK,反之亦然。

日後我們會改變做法,只需使用最基本的用戶端程式庫版本,以及特定版本的提供者程式庫,即可載入 SDK。

這樣做可以盡量減少片段,確保在成功載入封裝的 SDK 後,大多數 API 都能受到支援。