| Key concepts | Set up your development environment | Build an RE SDK | Consume the RE SDK | Testing, and building for distribution |
Laufzeitfähiges SDK erstellen
Sie müssen die folgenden Schritte ausführen, um ein SDK mit Laufzeit zu erstellen:
- Projektstruktur einrichten
- Projekt- und Modulabhängigkeiten vorbereiten
- SDK-Geschäftslogik hinzufügen
- SDK-APIs definieren
- Einstiegspunkt für das SDK angeben
Projektstruktur einrichten
Wir empfehlen, Ihr Projekt in die folgenden Module zu unterteilen:
- App-Modul: Die Test-App, mit der Sie Ihr SDK testen und entwickeln. Sie entspricht der echten App, die Ihre Kunden verwenden würden. Ihre App sollte eine Abhängigkeit vom vorhandenen Anzeigenbibliotheksmodul (laufzeitfähiges SDK) haben.
- Vorhandenes Anzeigenbibliotheksmodul (laufzeitfähiges SDK): Ein Android-Bibliotheksmodul, das Ihre vorhandene SDK-Logik enthält, die nicht für die Laufzeit konfiguriert ist, ein statisch verknüpftes SDK.
- Zuerst können die Funktionen aufgeteilt werden. Beispielsweise kann ein Teil des Codes von Ihrem vorhandenen SDK verarbeitet werden und ein Teil an das laufzeitfähige SDK weitergeleitet werden.
- Laufzeitfähiges Anzeigenbibliotheksmodul: Enthält die Geschäftslogik Ihres laufzeitfähigen SDKs. Sie kann in Android Studio als Android-Bibliotheksmodul erstellt werden.
- Laufzeitfähiges ASB-Modul: Definiert die Paketdaten, mit denen der Code des laufzeitfähigen SDK in ein ASB-Modul eingebunden wird.
- Sie muss manuell mit dem Typ com.android.privacy-sandbox-sdk erstellt werden. Dazu können Sie ein neues Verzeichnis erstellen.
- Dieses Modul sollte keinen Code und nur eine leere build.gradle-Datei mit Abhängigkeiten von Ihrem Anzeigenbibliotheksmodul mit Laufzeitunterstützung enthalten. Der Inhalt dieser Datei wird unter SDK vorbereiten beschrieben.
- Denken Sie daran, dieses Modul in die Datei „settings.gradle“ und in das bestehende Modul für die Anzeigenbibliothek aufzunehmen.
Die Projektstruktur in diesem Leitfaden ist nur ein Vorschlag. Sie können auch eine andere Struktur für Ihr SDK wählen und dieselben technischen Prinzipien anwenden. Sie können jederzeit weitere Module erstellen, um den Code in der App und den Bibliotheksmodulen zu modularisieren.
SDK vorbereiten
Um Ihr Projekt für die Entwicklung von SDKs mit Laufzeitunterstützung vorzubereiten, müssen Sie zuerst einige Tool- und Bibliotheksabhängigkeiten definieren:
- Abwärtskompatibilitätsbibliotheken für die SDK-Laufzeit, die Unterstützung für Geräte bieten, auf denen die Privacy Sandbox nicht verfügbar ist (Android 13 und niedriger) (
androidx.privacysandbox.sdkruntime:) - UI-Bibliotheken zur Unterstützung der Anzeigendarstellung (
androidx.privacysandbox.ui:) - SDK-Entwicklertools zur Unterstützung der SDK-API-Deklaration und Shim-Generierung (
androidx.privacysandbox.tools:)
Fügen Sie dieses Flag der Datei „gradle.properties“ Ihres Projekts hinzu, um die Möglichkeit zu aktivieren, laufzeitfähige SDKs zu erstellen.
# This enables the Privacy Sandbox for your project on Android Studio. android.experimental.privacysandboxsdk.enable=true android.experimental.privacysandboxsdk.requireServices=falseÄndern Sie die build.gradle-Datei Ihres Projekts, um die Jetpack-Hilfsbibliotheken und andere Abhängigkeiten einzuschließen:
// 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 }Aktualisieren Sie die Datei „build.gradle“ im Modul Laufzeitfähige Anzeigenbibliothek (RE SDK), damit diese Abhängigkeiten enthalten sind.
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" }Ersetzen Sie die Datei „build.gradle“ in Ihrem ASB-Modul mit Laufzeitunterstützung durch Folgendes:
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>') }Aktualisieren Sie die Datei „build.gradle“ in Ihrem bestehenden Modul für die Anzeigenbibliothek (RA SDK), um die folgenden Abhängigkeiten einzufügen:
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" }
Geschäftslogik für das SDK hinzufügen
Implementieren Sie die Geschäftslogik Ihres SDK wie gewohnt im Anzeigenbibliotheksmodul, das zur Laufzeit aktiviert wird.
Wenn Sie ein vorhandenes SDK migrieren, können Sie in dieser Phase so viel Geschäftslogik, Schnittstellen und systembezogene Funktionen wie gewünscht übertragen. Planen Sie jedoch eine vollständige Migration in der Zukunft ein.
Wenn Sie Zugriff auf den Speicher, die Google Play-Werbe-ID oder die App-Set-ID benötigen, lesen Sie die folgenden Abschnitte:
Speicher-APIs in Ihrem SDK verwenden
SDKs in der SDK Runtime können nicht mehr auf den internen Speicher einer App zugreifen, daraus lesen oder darin schreiben und umgekehrt.
Der SDK-Laufzeit wird ein eigener interner Speicherbereich zugewiesen, der vom Speicherbereich der App getrennt ist.
SDKs können über die Dateispeicher-APIs für das Context-Objekt, das von SandboxedSdkProvider#getContext() zurückgegeben wird, auf diesen separaten internen Speicher zugreifen.
SDKs können nur internen Speicher verwenden. Daher funktionieren nur APIs für internen Speicher wie Context.getFilesDir() oder Context.getCacheDir(). Weitere Beispiele finden Sie unter Zugriff über den internen Speicher.
Der Zugriff auf externen Speicher über die SDK-Laufzeit wird nicht unterstützt. Wenn Sie APIs aufrufen, um auf den externen Speicher zuzugreifen, wird entweder eine Ausnahme ausgelöst oder „null“ zurückgegeben. Die folgende Liste enthält einige Beispiele:
- Beim Zugriff auf Dateien über das Storage Access Framework wird eine SecurityException ausgelöst.
getExternalFilsDir()gibt immer „null“ zurück.
Sie müssen die von SandboxedSdkProvider.getContext() zurückgegebene Context für die Speicherung verwenden. Die Verwendung der File Storage API für eine andere Context-Objektinstanz, z. B. den Anwendungskontext, funktioniert nicht in allen Situationen wie erwartet.
Das folgende Code-Snippet zeigt, wie Speicher in der SDK-Laufzeit verwendet wird:
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" } }
Im separaten internen Speicher für jede SDK-Laufzeitumgebung hat jedes SDK ein eigenes Speicherverzeichnis. Der Speicher pro SDK ist eine logische Trennung des internen Speichers der SDK Runtime, die dabei hilft, zu erfassen, wie viel Speicherplatz jedes SDK verwendet.
Alle APIs für den internen Speicher im Context-Objekt geben für jedes SDK einen Speicherpfad zurück.
Auf die von den Google Play-Diensten bereitgestellte Werbe-ID zugreifen
Wenn Ihr SDK Zugriff auf die von Google Play-Diensten bereitgestellte Werbe-ID benötigt, verwenden Sie AdIdManager#getAdId(), um den Wert asynchron abzurufen.
Auf die von Google Play-Diensten bereitgestellte App-Set-ID zugreifen
Wenn Ihr SDK Zugriff auf die von Google Play-Diensten bereitgestellte App-Set-ID benötigt, verwenden Sie AppSetIdManager#getAppSetId(), um den Wert asynchron abzurufen.
SDK-APIs deklarieren
Damit Ihr Laufzeit-fähiges SDK außerhalb der Laufzeit zugänglich ist, müssen Sie APIs definieren, die von Clients (RA SDK oder Client-App) genutzt werden können.
Verwenden Sie Annotationen, um diese Schnittstellen zu deklarieren.
Annotationen
SDK-APIs müssen in Kotlin als Schnittstellen und Datenklassen mit den folgenden Annotationen deklariert werden:
| Annotationen | |
|---|---|
@PrivacySandboxService |
|
@PrivacySandboxInterface |
|
@PrivacySandboxValue |
|
@PrivacySandboxCallback |
|
Sie müssen diese Schnittstellen und Klassen an einer beliebigen Stelle im Modul der Anzeigenbibliothek definieren, die für die Laufzeit aktiviert ist.
Die Verwendung dieser Annotationen wird in den folgenden Abschnitten beschrieben.
@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() }
Unterstützte Typen
Für SDK-APIs, die zur Laufzeit aktiviert werden, werden die folgenden Typen unterstützt:
- Alle primitiven Typen in der Programmiersprache Java (z. B. int, long, char, boolean usw.)
- String
- Kotlin-Schnittstellen, die mit
@PrivacySandboxInterfaceoder@PrivacySandboxCallbackannotiert sind - Mit
@PrivacySandboxValueannotierte Kotlin-Datenklassen - java.lang.List: Alle Elemente in der Liste müssen einen der unterstützten Datentypen haben.
Es gibt einige zusätzliche Einschränkungen:
- Datenklassen, die mit
@PrivacySandboxValueannotiert sind, dürfen keine Felder vom Typ@PrivacySandboxCallbackenthalten. - Rückgabetypen dürfen keine mit
@PrivacySandboxCallbackannotierten Typen enthalten. - Die Liste darf keine Elemente von Typen enthalten, die mit
@PrivacySandboxInterfaceoder@PrivacySandboxCallbackannotiert sind.
Asynchrone APIs
Da die SDK-APIs immer einen Aufruf an einen separaten Prozess ausführen, müssen wir dafür sorgen, dass diese Aufrufe den aufrufenden Thread des Clients nicht blockieren.
Dazu müssen alle Methoden in den mit @PrivacySandboxService, @PrivacySandboxInterface und @PrivacySandboxCallback annotierten Schnittstellen explizit als asynchrone APIs deklariert werden.
Asynchrone APIs können in Kotlin auf zwei Arten implementiert werden:
- Verwenden Sie Suspend-Funktionen.
- Rückrufe akzeptieren, die benachrichtigt werden, wenn der Vorgang abgeschlossen ist oder andere Ereignisse während des Fortschritts des Vorgangs auftreten. Der Rückgabetyp der Funktion muss eine Einheit sein.
Ausnahmen
Die SDK-APIs unterstützen keine Form von geprüften Ausnahmen.
Der generierte Shim-Code fängt alle Laufzeit-Exceptions ab, die vom SDK ausgelöst werden, und löst sie als PrivacySandboxException an den Client aus. Die Ursache ist darin enthalten.
UI-Bibliothek
Wenn Sie Schnittstellen haben, die Anzeigen darstellen, z. B. ein Banner, müssen Sie auch die SandboxedUiAdapter-Schnittstelle implementieren, um Sitzungen für die geladene Anzeige zu öffnen.
Diese Sitzungen bilden einen Side-Channel zwischen dem Client und dem SDK und erfüllen zwei Hauptzwecke:
- Benachrichtigungen erhalten, wenn sich die Benutzeroberfläche ändert.
- Informieren Sie den Kunden über alle Änderungen an der Darstellung der Benutzeroberfläche.
Da der Client die mit @PrivacySandboxService annotierte Schnittstelle für die Kommunikation mit Ihrem SDK verwenden kann, können dieser Schnittstelle alle APIs zum Laden von Anzeigen hinzugefügt werden.
Wenn der Client das Laden einer Anzeige anfordert, laden Sie die Anzeige und geben Sie eine Instanz der Schnittstelle zurück, die SandboxedUiAdapter implementiert. So kann der Client Sitzungen für diese Anzeige anfordern.
Wenn der Client eine Sitzung öffnen möchte, kann Ihr SDK mit aktivierter Laufzeitumgebung mithilfe der Anzeigenantwort und des bereitgestellten Kontexts eine Anzeigenansicht erstellen.
Erstellen Sie dazu eine Klasse, die die SandboxedUiAdapter.Session-Schnittstelle implementiert. Wenn SandboxedUiAdapter.openSession() aufgerufen wird, rufen Sie client.onSessionOpened() auf und übergeben Sie eine Instanz der Session-Klasse als Parameter.
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)
}
}
}
Diese Klasse erhält auch Benachrichtigungen, wenn eine Änderung der Benutzeroberfläche erfolgt. Mit dieser Klasse können Sie die Größe der Anzeige ändern oder feststellen, wann sich die Konfiguration geändert hat.
Weitere Informationen zu APIs für die UI-Präsentation in der Laufzeit
Unterstützung bei Aktivitäten
Wenn Sie SDK-eigene Aktivitäten aus der Privacy Sandbox heraus starten möchten, müssen Sie die SDK-API so ändern, dass sie ein SdkActivityLauncher-Objekt empfängt, das ebenfalls von der UI-Bibliothek bereitgestellt wird.
Die folgende SDK-API sollte beispielsweise Aktivitäten starten. Daher wird der Parameter SdkActivityLauncher erwartet:
@PrivacySandboxInterface
interface FullscreenAd {
suspend fun show(activityLauncher: SdkActivityLauncher)
}
SDK-Einstiegspunkt
Die abstrakte Klasse SandboxedSdkProvider kapselt die API, die die SDK-Laufzeit für die Interaktion mit den darin geladenen SDKs verwendet.
Ein laufzeitfähiges SDK muss diese abstrakte Klasse implementieren, um einen Einstiegspunkt für die SDK-Laufzeit zu generieren, damit diese mit dem SDK kommunizieren kann.
Zur Unterstützung der Abwärtskompatibilität haben wir die folgenden Klassen eingeführt:
SandboxedSdkProviderAdapter, dasSandboxedSdkProvidererweitert und SDK-Ladeanfragen unabhängig von der Verfügbarkeit der SDK-Laufzeit verarbeitet. Wird intern verwendet und im ASB-Modul deklariert.SandboxedSdkProviderCompat, eine abstrakte Klasse, die die Schnittstelle vonSandboxedSdkProviderimitiert.
Weitere Informationen zur Abwärtskompatibilität für die SDK-Laufzeit
Die Tools zur Shim-Generierung fügen eine weitere Abstraktionsebene hinzu: Sie generieren eine abstrakte Klasse namens AbstractSandboxedSdkProvider anhand der Schnittstelle, die Sie mit @PrivacySandboxService annotiert haben.
Diese Klasse erweitert SandboxedSdkProviderCompat und befindet sich im selben Paket wie die annotierte Schnittstelle.
// Auto-generated code.
abstract class AbstractSandboxedSdkProvider : SandboxedSdkProviderCompat {
abstract fun createMySdk(context: Context): MySdk
}
Diese generierte Klasse macht eine einzelne abstrakte Factory-Methode verfügbar, die ein Context akzeptiert und erwartet, dass die mit dem Einstiegspunkt annotierte Schnittstelle zurückgegeben wird.
Diese Methode wird nach Ihrer @PrivacySandboxService-Schnittstelle benannt, wobei dem Namen create vorangestellt wird. Wenn Ihre Schnittstelle beispielsweise MySdk heißt, generieren die Tools createMySdk.
Damit der Einstiegspunkt vollständig verbunden wird, müssen Sie eine Implementierung der mit @PrivacySandboxService annotierten Schnittstelle im Laufzeit-fähigen SDK für die generierte AbstractSandboxedSdkProvider bereitstellen.
class MySdkSandboxedSdkProvider : AbstractSandboxedSdkProvider() {
override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
}
Änderungen am ASB-Modul
Sie müssen den vollständig qualifizierten Klassennamen Ihrer Implementierung von SandboxedSdkProviderCompat im Feld compatSdkProviderClassName der build.gradle-Datei Ihres ASB-Moduls deklarieren.
Dies ist die Klasse, die Sie im vorherigen Schritt implementiert haben. Sie würden die build.gradle-Datei in Ihrem ASB-Modul so ändern:
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"
}
Schritt 2: Entwicklungsumgebung einrichten Schritt 4: Laufzeit-fähiges SDK verwenden