SDK ランタイムの下位互換性

このドキュメントでは、デベロッパーによる SDK ランタイムへの移行を支援する新しい Jetpack ライブラリを提案します。以前の Android プラットフォーム バージョンに対する SDK ランタイムのサポート(ビルドから実行まで)と、デベロッパーが想定されるランタイム環境の違いや制限事項について説明します。このライブラリにより、デベロッパーはアプリまたは 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 は、アプリが依存するすべての SDK を含むアプリ APK セットのバリアントを 1 つ以上ビルドします。各 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 を追加し、SdkSandboxManager の代わりに SdkSandboxManagerCompat を使用します。

アプリが SDK を読み込もうとすると、ライブラリはまず、ビルド中に SDK がアプリにバンドルされたかどうかを確認します。バンドルされている場合、ライブラリは SDK の分割から SDK を抽出し、アプリプロセスに読み込みます。SDK がアプリにバンドルされていない場合、ライブラリは SDK を読み込むためにプラットフォーム API をデリゲートします。

アセットから SDK を抽出する

バンドルされた SDK を読み込もうとすると、SDK ランタイム クライアント ライブラリは、SDK の DEX ファイルがすでにデバイス ストレージ(code_cache)に抽出されているかどうかを確認し、そうでない場合はアセットから抽出します。

このライブラリは通常、アプリのインストールまたは更新後にファイルを 1 回だけ抽出します。

使用可能なストレージ容量が許容しきい値(現在 100 MB)未満で、DEX ファイルが抽出されていない場合、ライブラリは、サポートされているデバイス(API 27 以降)のアセットから SDK を直接読み込みます。そのため、メモリ フットプリントが大きくなります。

SDK クラスのクラスローダー

SDK とアプリクラスとの競合を回避するため、すべての SDK クラスは、メインアプリのクラスローダーから完全に独立した個別のクラスローダーを使用して読み込まれます。

現在の SDK ランタイム設計では、アプリと SDK の間のすべての通信は Binder IPC 呼び出しを使用して行われます。バンドルされた SDK に同じ SDK Binder オブジェクトが使用されます。Binder トランザクションのシリアル化により、アプリ デベロッパーは SDK Binder オブジェクトをアプリ側の SDK Binder インターフェースにキャストできます。

その他の内部操作(SDK の初期化、SDK へのコントローラ API の提供など)の場合、ライブラリはリフレクションと動的プロキシを使用して、さまざまなクラスローダーをまたいで動作します。

SDK 環境

SDKRuntime Provider ライブラリは、SDK デベロッパーに API を提供します。これらの API はプラットフォーム API に似ていますが、SDK ランタイム環境と SDKRuntime クライアント ライブラリの両方で SDK を読み込むことができます。

ライブラリ SDK を使用するには、androidx.privacysandbox.sdkruntime:sdkruntime-provider 依存関係を追加し、SandboxedSdkProvider ではなく SandboxedSdkProviderCompat を拡張する必要があります。

また、互換プロバイダを SDK ランタイム環境に読み込めるように、SandboxedSdkProviderAdapter を SDK プロバイダとして使用する必要もあります。

SdkSandboxControllerCompat は、SDK が SDK ランタイムに読み込まれるとプラットフォーム API にデリゲートされ、SDK がバンドルされた SDK として読み込まれると SDKRuntime クライアント ライブラリにデリゲートされます。

バンドルされた SDK の場合、ライブラリは SDK ランタイム環境と同様の動作をエミュレートする方法で SDK 環境を変更します。

以降のセクションでは、SDKRuntime クライアント ライブラリによって SDK が読み込まれたときに想定される動作について説明します。

SDK リソース

SDK リソース(res/)は、SDK がアプリプロセスで読み込まれるときにサポートされます。 Bundletool は、すべての SDK のリソースをアプリリソースと統合します。

競合を回避するため、すべてのリソース ID の packageId 接頭辞を変更して、SDK リソースを再マッピングします。

SDK が SDKRuntime クライアント ライブラリによって読み込まれると、ランタイムで packageId が更新され、R クラスを使用して、再マッピングされたリソースをアドレス指定できるようになります。

Java リソース

SDK がアプリプロセスで読み込まれると、Java リソースがサポートされます。Bundletool は、すべての SDK Java リソースをアプリアセット内の特別なディレクトリにコピーします。SDKRuntime クライアント ライブラリは、中間クラスローダーを使用して、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

SDKRuntime クライアント ライブラリは、アプリプロセスで読み込まれるバンドル SDK に SdkSandboxControllerCompat 実装を提供します。

クライアント ライブラリで API がサポートされていない場合(アプリ バージョンより新しいバージョンのライブラリでビルドされた SDK など)は、最適なフォールバックが使用されます(no-op または例外)。

バージョニング

SDKRuntime クライアント ライブラリがバンドルされた SDK を読み込むと、SDK 内の SDKRuntime プロバイダ ライブラリを使用して handshake を実行します。handshake 中に、ライブラリはバージョンを交換し、使用できない API を最適なフォールバック(NoOps または例外)に置き換えるように動作を調整します。

アプリと SDK の両方のデベロッパーには、最新バージョンのライブラリを使用することを強くおすすめします。そうしないと、両方の部分でサポートを必要とする機能が利用できない可能性があります。

SDKRuntime クライアント ライブラリのどのバージョンでも、任意のバージョンの SDKRuntime プロバイダ ライブラリで SDK を読み込むことができます。その逆も可能です。

今後は、特定のバージョンのプロバイダ ライブラリで SDK を読み込むために必要なクライアント ライブラリの最小バージョンに変更されます。

これにより、断片化が最小限に抑えられ、バンドルされた SDK が正常に読み込まれた場合にほとんどの API がサポートされます。