| Key concepts | Set up your development environment | Build an RE SDK | Consume the RE SDK | Testing, and building for distribution |
ランタイム対応 SDK をビルドする
ランタイム対応 SDK をビルドするには、次の手順を完了する必要があります。
プロジェクト構造を設定する
プロジェクトは次のモジュールに編成することをおすすめします。
- アプリ モジュール - アプリのテストと開発に使用するテストアプリ 実際のアプリ クライアントのものを表す SDK。アプリは 既存の広告ライブラリ モジュール(ランタイム対応 SDK)と依存関係がある。
- 既存の広告ライブラリ モジュール(ランタイム対応 SDK) - Android ライブラリ モジュール
既存の「ランタイムが有効になっていない」ことを静的に行う必要が
リンクされています。
- 最初は、機能を分割できます。たとえば、一部のコードは既存の SDK で処理でき、一部のコードはランタイム対応 SDK に転送できます。
- ランタイム対応の広告ライブラリ モジュール - ランタイム対応の SDK が含まれています サポートします。これは、Android Studio で Android ライブラリ モジュールとして作成できます。
- ランタイム対応 ASB モジュール - アプリケーションをバンドルするパッケージ データを定義します。
ランタイム対応 SDK コードを ASB に変換します。
- com.android.privacy-sandbox-sdk タイプを使用して手動で作成する必要があります。そのためには、 作成します。
- このモジュールにはコードを含めず、ランタイムで有効な広告ライブラリ モジュールへの依存関係を含む空の build.gradle ファイルのみを含めます。このファイルの内容は、SDK を準備するで定義されています。
- このモジュールは必ず settings.gradle ファイルと 既存の広告ライブラリモジュールに
このガイドのプロジェクト構造はあくまでも目安であり、別の構造を選択できます。 同じ技術原則を適用します。いつでも他の追加モジュールを作成して、アプリ内のコードとライブラリ モジュールをモジュール化できます。
SDK を準備する
ランタイム対応 SDK 開発用にプロジェクトを準備するには、以下を行う必要があります。 まず、ツールとライブラリの依存関係を定義します。
- SDK ランタイムの下位互換性ライブラリは、
プライバシー サンドボックスがインストールされていないデバイス(Android 13 以前)
(
androidx.privacysandbox.sdkruntime:) - 広告の表示をサポートする UI ライブラリ(
androidx.privacysandbox.ui:) - SDK API 宣言と shim 生成をサポートする SDK デベロッパー ツール(
androidx.privacysandbox.tools:)
このフラグをプロジェクトの gradle.properties ファイルに追加して、ランタイム対応 SDK を作成する機能を有効にします。
# This enables the Privacy Sandbox for your project on Android Studio. android.experimental.privacysandboxsdk.enable=true android.experimental.privacysandboxsdk.requireServices=falseヘルパー Jetpack ライブラリとその他の依存関係を含めるように、プロジェクトの build.gradle を変更します。
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = '1.9.10' ext.ksp_version = "$kotlin_version-1.0.13" ext.privacy_sandbox_activity_version = "1.0.0-alpha01" ext.privacy_sandbox_sdk_runtime_version = "1.0.0-alpha13" ext.privacy_sandbox_tools_version = "1.0.0-alpha09" ext.privacy_sandbox_ui_version = "1.0.0-alpha09" repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } plugins { id 'com.android.application' version '8.4.0-alpha13' apply false id 'com.android.library' version '8.4.0-alpha13' apply false // These two plugins do annotation processing and code generation for the sdk-implementation. id 'androidx.privacysandbox.library' version '1.0.0-alpha02' apply false id 'com.google.devtools.ksp' version "$ksp_version" apply false id 'org.jetbrains.kotlin.jvm' version '1.9.10' apply false } task clean(type: Delete) { delete rootProject.buildDir }ランタイム対応広告ライブラリ(RE SDK)モジュールの build.gradle ファイルを更新して、これらの依存関係を含めます。
dependencies { // This allows Android Studio to parse and validate your SDK APIs. ksp "androidx.privacysandbox.tools:tools-apicompiler:$privacy_sandbox_tools_version" // This contains the annotation classes to decorate your SDK APIs. implementation "androidx.privacysandbox.tools:tools:$privacy_sandbox_tools_version" // This is runtime dependency required by the generated server shim code for // backward compatibility. implementation "androidx.privacysandbox.sdkruntime:sdkruntime-provider:$privacy_sandbox_sdk_runtime_version" // These are runtime dependencies required by the generated server shim code as // they use Kotlin. implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1' // This is the core part of the UI library to help with UI notifications. implementation "androidx.privacysandbox.ui:ui-core:$privacy_sandbox_ui_version" // This helps the SDK open sessions for the ad. implementation "androidx.privacysandbox.ui:ui-provider:$privacy_sandbox_ui_version" // This is needed if your SDK implements mediation use cases implementation "androidx.privacysandbox.ui:ui-client:$privacy_sandbox_ui_version" }ランタイム対応の ASB モジュールの build.gradle ファイルを次のように置き換えます。
plugins { id 'com.android.privacy-sandbox-sdk' } android { compileSdk 34 minSdk 21 bundle { // This is the package name of the SDK that you want to publish. // This is used as the public identifier of your SDK. // You use this later on to load the runtime-enabled SDK packageName = '<package name of your runtime-enabled SDK>' // This is the version of the SDK that you want to publish. // This is used as the public identifier of your SDK version. setVersion(1, 0, 0) // SDK provider defined in the SDK Runtime library. // This is an important part of the future backwards compatibility // support, most SDKs won't need to change it. sdkProviderClassName = "androidx.privacysandbox.sdkruntime.provider.SandboxedSdkProviderAdapter" // This is the class path of your implementation of the SandboxedSdkProviderCompat class. // It's the implementation of your runtime-enabled SDK's entry-point. // If you miss this step, your runtime-enabled SDK will fail to load at runtime: compatSdkProviderClassName = "<your-sandboxed-sdk-provider-compat-fully-qualified-class-name>" } } dependencies { // This declares the dependency on your runtime-enabled ad library module. include project(':<your-runtime-enabled-ad-library-here>') }既存の広告ライブラリ(RA SDK)モジュールの build.gradle ファイルを更新して、次の依存関係を含めます。
dependencies { // This declares the client's dependency on the runtime-enabled ASB module. // ⚠️ Important: We depend on the ASB module, not the runtime-enabled module. implementation project(':<your-runtime-enabled-asb-module-here>') // Required for backwards compatibility on devices where SDK Runtime is unavailable. implementation "androidx.privacysandbox.sdkruntime:sdkruntime-client:$privacy_sandbox_sdk_runtime_version" // This is required to display banner ads using the SandboxedUiAdapter interface. implementation "androidx.privacysandbox.ui:ui-core:$privacy_sandbox_ui_version" implementation "androidx.privacysandbox.ui:ui-client:$privacy_sandbox_ui_version" // This is required to use SDK ActivityLaunchers. implementation "androidx.privacysandbox.activity:activity-core:$privacy_sandbox_activity_version" implementation "androidx.privacysandbox.activity:activity-client:$privacy_sandbox_activity_version" }
SDK ビジネス ロジックを追加する
ランタイムで有効な広告ライブラリ モジュール内で通常どおりに、SDK のビジネス ロジックを実装します。
移行する既存の SDK がある場合は、この段階でビジネス ロジック、インターフェース、システム向け関数を可能な限り移行しますが、将来の完全な移行を考慮してください。
ストレージ、Google Play 広告 ID、アプリセット ID にアクセスする必要がある場合は、以下のセクションをご覧ください。
SDK でストレージ API を使用する
SDK ランタイムの SDK は、アプリの内部ストレージに対するアクセス、読み取り、書き込みを行えなくなります その逆も同様です。
SDK ランタイムには、アプリと独立した内部ストレージ領域が割り当てられます。
SDK は、SandboxedSdkProvider#getContext() によって返される Context オブジェクトのファイル ストレージ API を使用して、この個別の内部ストレージにアクセスできます。
SDK は内部ストレージのみを使用できるため、Context.getFilesDir() や
Context.getCacheDir() の職場。その他の例:
内部ストレージからのアクセス。
SDK ランタイムから外部ストレージへのアクセスはサポートされていません。API を呼び出して外部ストレージにアクセスすると、例外がスローされるか、null が返されます。次のリストに例を示します。
- ストレージ アクセス フレームワークを使用してファイルにアクセスすると、SecurityException がスローされます。
getExternalFilsDir()は常に null を返します。
ストレージには、SandboxedSdkProvider.getContext() から返される Context を使用する必要があります。アプリのコンテキストなど、他の Context オブジェクト インスタンスでファイル ストレージ API を使用した場合、すべての状況で想定どおりに動作するとは限りません。
次のコード スニペットは、SDK ランタイムでストレージを使用する方法を示しています。
class SdkServiceImpl(private val context: Context) : SdkService { override suspend fun getMessage(): String = "Hello from Privacy Sandbox!" override suspend fun createFile(sizeInMb: Int): String { val path = Paths.get( context.dataDir.path, "file.txt" ) withContext(Dispatchers.IO) { Files.deleteIfExists(path) Files.createFile(path) val buffer = ByteArray(sizeInMb * 1024 * 1024) Files.write(path, buffer) } val file = File(path.toString()) val actualFileSize: Long = file.length() / (1024 * 1024) return "Created $actualFileSize MB file successfully" } }
SDK ランタイムごとに個別の内部ストレージ内に、各 SDK に独自のストレージ ディレクトリがあります。SDK ごとのストレージは、SDK ランタイムの内部ストレージを論理的に分離したものです。これにより、各 SDK が使用するストレージの量を考慮できます。
Context オブジェクトのすべての内部ストレージ API は、SDK ごとにストレージ パスを返します。
Google Play 開発者サービスから提供された広告 ID にアクセスする
SDK が Google Play 開発者サービスから提供された広告 ID にアクセスする必要がある場合は、AdIdManager#getAdId() を使用して値を非同期で取得します。
Google Play 開発者サービスから提供されたアプリセット ID にアクセスする
SDK が Google Play 開発者サービスから提供されたアプリセット ID にアクセスする必要がある場合は、次のコマンドを使用します。
AppSetIdManager#getAppSetId(): 値を非同期で取得します。
SDK API を宣言する
ランタイム対応 SDK にランタイムの外部からアクセスできるようにするには、以下を行う必要があります。 クライアント(RA SDK またはクライアント アプリ)が使用できる API を定義します。
これらのインターフェースを宣言するには、アノテーションを使用します。
アノテーション
Kotlin では、SDK API を、 次のアノテーション:
| アノテーション | |
|---|---|
@PrivacySandboxService |
|
@PrivacySandboxInterface |
|
@PrivacySandboxValue |
|
@PrivacySandboxCallback |
|
これらのインターフェースとクラスは、Terraform 自体の 実装する必要があります。
これらのアノテーションの使用方法については、次のセクションをご覧ください。
@PrivacySandboxService
@PrivacySandboxService interface SdkService { suspend fun getMessage(): String suspend fun createFile(sizeInMb: Int): String suspend fun getBanner(request: SdkBannerRequest, requestMediatedAd: Boolean): SdkSandboxedUiAdapter? suspend fun getFullscreenAd(): FullscreenAd }
@PrivacySandboxInterface
@PrivacySandboxInterface interface SdkSandboxedUiAdapter : SandboxedUiAdapter
@PrivacySandboxValue
@PrivacySandboxValue data class SdkBannerRequest( /** The package name of the app. */ val appPackageName: String, /** * An [SdkActivityLauncher] used to launch an activity when the banner is clicked. */ val activityLauncher: SdkActivityLauncher, /** * Denotes if a WebView banner ad needs to be loaded. */ val isWebViewBannerAd: Boolean )
@PrivacySandboxCallback
@PrivacySandboxCallback interface InAppMediateeSdkInterface { suspend fun show() }
サポートされているタイプ
ランタイム対応の SDK API は、次のタイプをサポートしています。
- Java プログラミング言語のすべてのプリミティブ型(int、long、char、boolean など)
- 文字列
@PrivacySandboxInterfaceまたは@PrivacySandboxCallback@PrivacySandboxValueアノテーション付きの Kotlin データクラス- java.lang.List - List 内のすべての要素は、サポートされているデータ型のいずれかである必要があります。
いくつか追加の注意点があります。
@PrivacySandboxValueアノテーション付きのデータクラスには、次のフィールドを含めることはできません。 タイプ@PrivacySandboxCallback- 戻り値の型に
@PrivacySandboxCallbackアノテーション付きの型を含めることはできません - リストに
@PrivacySandboxInterfaceまたは@PrivacySandboxCallbackでアノテーションされたタイプの要素を含めることはできません
非同期 API
SDK API は常に別のプロセスを呼び出すため、これらの呼び出しがクライアントの呼び出しスレッドをブロックしないようにする必要があります。
これを実現するため、
@PrivacySandboxService、@PrivacySandboxInterface、@PrivacySandboxCallback
非同期 API として明示的に宣言する必要があります。
Kotlin では、次の 2 つの方法で非同期 API を実装できます。
- suspend 関数を使用します。
- オペレーションの完了時や、オペレーションの進行中の他のイベントの通知を受け取るコールバックを受け入れます。関数の戻り値の型 ユニットである必要があります。
例外
SDK API では、いかなる形式のチェック例外もサポートされません。
生成された shim コードは、SDK によってスローされたランタイム例外をキャッチし、
その情報とともに PrivacySandboxException としてクライアントにスローされます。
その中に隠れた原因を突き止めるのです。
UI ライブラリ
バナーなど、広告を表すインターフェースがある場合は、読み込まれた広告のセッションを開くように SandboxedUiAdapter インターフェースを実装する必要があります。
これらのセッションは、クライアントと SDK の間のサイドチャネルを形成し、 主に次の 2 つの目的を果たします
- UI の変更が発生するたびに通知を受け取ります。
- UI の表示が変更された場合は、クライアントに通知します。
クライアントは、@PrivacySandboxService アノテーション付きのインターフェースを使用して、
SDK と通信する場合は、広告を読み込むための API を
行うことができます。
クライアントが広告の読み込みをリクエストすると、広告を読み込み、
SandboxedUiAdapter を実装するインターフェース。これにより、クライアントはその広告の開始セッションをリクエストできます。
クライアントがセッションの開始をリクエストすると、ランタイム対応の SDK は、広告レスポンスと指定されたコンテキストを使用して広告ビューを作成できます。
そのためには、SandboxedUiAdapter.Session インターフェースを実装するクラスを作成し、SandboxedUiAdapter.openSession() が呼び出されたら、必ず client.onSessionOpened() を呼び出し、Session クラスのインスタンスをパラメータとして渡します。
class SdkSandboxedUiAdapterImpl(
private val sdkContext: Context,
private val request: SdkBannerRequest,
) : SdkSandboxedUiAdapter {
override fun openSession(
context: Context,
windowInputToken: IBinder,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
clientExecutor: Executor,
client: SandboxedUiAdapter.SessionClient
) {
val session = SdkUiSession(clientExecutor, sdkContext, request)
clientExecutor.execute {
client.onSessionOpened(session)
}
}
}
このクラスは、UI が変更されるたびに通知も受け取ります。このクラスを使用すると、広告のサイズ変更や、構成が変更されたときの確認などを行うことができます。
ランタイムでの UI 表示 API の詳細をご確認ください。
アクティビティのサポート
プライバシー サンドボックスから SDK 所有のアクティビティを開始するには、同じく UI ライブラリによって提供される SdkActivityLauncher オブジェクトを受け取るように SDK API を変更する必要があります。
たとえば、次の SDK API はアクティビティを起動する必要があるため、SdkActivityLauncher パラメータが必要です。
@PrivacySandboxInterface
interface FullscreenAd {
suspend fun show(activityLauncher: SdkActivityLauncher)
}
SDK のエントリ ポイント
抽象クラス SandboxedSdkProvider
SDK ランタイムが読み込まれた SDK の操作に使用する API をカプセル化します。
ランタイム対応 SDK は、この抽象クラスを実装する必要があります。これにより、SDK ランタイムが通信できるエントリ ポイントが生成されます。
下位互換性をサポートするために、次のクラスを導入しました。
SandboxedSdkProviderAdapter:SandboxedSdkProviderを拡張し、SDK ランタイムの可用性に関係なく SDK 読み込みリクエストを処理します。これは内部で使用され、ASB モジュールで宣言されます。SandboxedSdkProviderCompat:SandboxedSdkProviderのインターフェースを模倣する抽象クラス。
shim 生成ツールは、別の抽象化レイヤを追加します。つまり、@PrivacySandboxService アノテーションを付けたインターフェースを使用して、AbstractSandboxedSdkProvider という抽象クラスを生成します。
このクラスは SandboxedSdkProviderCompat を拡張し、アノテーション付きインターフェースと同じパッケージ下にあります。
// Auto-generated code.
abstract class AbstractSandboxedSdkProvider : SandboxedSdkProviderCompat {
abstract fun createMySdk(context: Context): MySdk
}
この生成されたクラスは、Context を受け取り、エントリ ポイントのアノテーション付きインターフェースが返されることを想定する単一の抽象ファクトリー メソッドを公開します。
このメソッドは、@PrivacySandboxService インターフェースに基づいて名前が付けられ、
create を名前に追加します。たとえば、インターフェース名が MySdk の場合、ツールは
createMySdk を生成します。
エントリ ポイントを完全に接続するには、生成された AbstractSandboxedSdkProvider に、ランタイム対応 SDK の @PrivacySandboxService アノテーション付きインターフェースの実装を提供する必要があります。
class MySdkSandboxedSdkProvider : AbstractSandboxedSdkProvider() {
override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
}
ASB モジュールの変更点
ASB モジュールの build.gradle の compatSdkProviderClassName フィールドで、SandboxedSdkProviderCompat の実装の完全修飾クラス名を宣言する必要があります。
前のステップで実装したクラスです。ASB モジュールで build.gradle を次のように変更します。
bundle {
packageName = '<package name of your runtime-enabled SDK>'
setVersion(1, 0, 0)
// SDK provider defined in the SDK Runtime library.
sdkProviderClassName = "androidx.privacysandbox.sdkruntime.provider.SandboxedSdkProviderAdapter"
// This is the class that extends AbstractSandboxedSdkProvider,
// MySdkSandboxProvider as per the example provided.
compatSdkProviderClassName = "com.example.mysdk.MySdkSandboxProvider"
}
ステップ 2: 開発環境を設定する ステップ 4: ランタイム対応 SDK を使用する