| Key concepts | Set up your development environment | Build an RE SDK | Consume the RE SDK | Testing, and building for distribution |
Tworzenie pakietu SDK wywoływanego w czasie działania aplikacji
Aby utworzyć pakiet SDK z włączonym środowiskiem wykonawczym, musisz wykonać te czynności:
- Konfigurowanie struktury projektu
- Przygotuj projekt i zależności modułów
- Dodawanie logiki biznesowej pakietu SDK
- Określ interfejsy API pakietu SDK
- Określanie punktu wejścia pakietu SDK
Konfigurowanie struktury projektu
Zalecamy podzielenie projektu na te moduły:
- Moduł aplikacji – aplikacja testowa, której używasz do testowania i rozwijania pakietu SDK. Odzwierciedla ona to, co mają Twoi klienci korzystający z prawdziwej aplikacji. Aplikacja powinna być zależna od istniejącego modułu biblioteki reklam (pakiet SDK z włączonym środowiskiem wykonawczym).
- Istniejący moduł biblioteki reklam (pakiet SDK uwzględniający środowisko wykonawcze) – moduł biblioteki Androida zawierający istniejącą logikę pakietu SDK „nieobsługującego środowiska wykonawczego”, czyli pakiet SDK połączony statycznie.
- Na początek można podzielić możliwości. Na przykład część kodu może być obsługiwana przez dotychczasowy pakiet SDK, a część może być kierowana do pakietu SDK z włączonym środowiskiem wykonawczym.
- Moduł biblioteki reklamowej używanej w czasie działania aplikacji – zawiera logikę biznesową pakietu SDK używanego w czasie działania aplikacji. Można go utworzyć w Android Studio jako moduł biblioteki Androida.
- Moduł ASB używany w czasie działania aplikacji – określa dane pakietu, które mają być dołączone do kodu pakietu SDK używanego w czasie działania aplikacji w formacie ASB.
- Musisz utworzyć go ręcznie, używając typu com.android.privacy-sandbox-sdk. Możesz to zrobić, tworząc nowy katalog.
- Ten moduł nie powinien zawierać żadnego kodu, tylko pusty plik build.gradle z zależnościami od modułu biblioteki reklamowej z włączonym środowiskiem wykonawczym. Zawartość tego pliku jest określona w sekcji Przygotowywanie pakietu SDK.
- Pamiętaj, aby uwzględnić ten moduł w pliku settings.gradle i w istniejącym module biblioteki reklam.
Struktura projektu w tym przewodniku jest tylko sugestią. Możesz wybrać inną strukturę pakietu SDK i zastosować te same zasady techniczne. Zawsze możesz utworzyć dodatkowe moduły, aby podzielić kod w aplikacji i modułach biblioteki.
Przygotowywanie pakietu SDK
Aby przygotować projekt do tworzenia pakietów SDK z włączonym środowiskiem wykonawczym, musisz najpierw zdefiniować niektóre narzędzia i zależności bibliotek:
- Biblioteki zgodności wstecznej środowiska wykonawczego SDK, które zapewniają obsługę urządzeń bez Piaskownicy prywatności (Android 13 i starsze wersje)(
androidx.privacysandbox.sdkruntime:) - Biblioteki interfejsu do obsługi wyświetlania reklam (
androidx.privacysandbox.ui:) - Narzędzia dla deweloperów pakietu SDK do obsługi deklaracji interfejsu API pakietu SDK i generowania elementów shim (
androidx.privacysandbox.tools:)
Aby włączyć możliwość tworzenia pakietów SDK z włączonym środowiskiem wykonawczym, dodaj ten flagę do pliku gradle.properties projektu.
# This enables the Privacy Sandbox for your project on Android Studio. android.experimental.privacysandboxsdk.enable=true android.experimental.privacysandboxsdk.requireServices=falseZmodyfikuj plik build.gradle projektu, aby uwzględnić biblioteki pomocnicze Jetpack i inne zależności:
// 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 }Zaktualizuj plik build.gradle w module biblioteki reklam wywoływanych w czasie działania aplikacji (RE SDK), aby uwzględnić te zależności.
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" }Zastąp plik build.gradle w module ASB z włączonym środowiskiem wykonawczym tym kodem:
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>') }Zaktualizuj plik build.gradle w istniejącym module biblioteki reklam (pakiet SDK RA), aby uwzględnić te zależności:
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" }
Dodawanie logiki biznesowej pakietu SDK
Zaimplementuj logikę biznesową pakietu SDK w module biblioteki reklamowej z włączonym środowiskiem wykonawczym w taki sam sposób, jak zwykle.
Jeśli masz już pakiet SDK, który chcesz przenieść, możesz na tym etapie przenieść dowolną część logiki biznesowej, interfejsu i funkcji systemowych, ale pamiętaj, że w przyszłości musisz przeprowadzić pełną migrację.
Jeśli potrzebujesz dostępu do pamięci, identyfikatora reklamowego Google Play lub identyfikatora zestawu aplikacji, zapoznaj się z tymi sekcjami:
Korzystanie z interfejsów API pamięci w pakiecie SDK
Pakiety SDK w środowisku wykonawczym SDK nie mogą już uzyskiwać dostępu do pamięci wewnętrznej aplikacji, odczytywać ani zapisywać w niej danych i na odwrót.
Środowisko wykonawcze SDK ma własny obszar pamięci wewnętrznej, oddzielony od aplikacji.
Pakiety SDK mają dostęp do tego oddzielnego wewnętrznego miejsca na dane za pomocą interfejsów API do przechowywania plików w obiekcie Context zwracanym przez SandboxedSdkProvider#getContext().
Pakiety SDK mogą korzystać tylko z pamięci wewnętrznej, więc działają tylko interfejsy API pamięci wewnętrznej, np. Context.getFilesDir() lub Context.getCacheDir(). Więcej przykładów znajdziesz w sekcji Dostęp z pamięci wewnętrznej.
Dostęp do pamięci zewnętrznej z poziomu środowiska wykonawczego pakietu SDK nie jest obsługiwany. Wywoływanie interfejsów API w celu uzyskania dostępu do pamięci zewnętrznej spowoduje zgłoszenie wyjątku lub zwrócenie wartości null. Oto kilka przykładów:
- Dostęp do plików za pomocą platformy Storage Access Framework powoduje zgłoszenie wyjątku SecurityException.
- Funkcja
getExternalFilsDir()zawsze zwraca wartość null.
Musisz użyć wartości Context zwróconej przez SandboxedSdkProvider.getContext() na potrzeby przechowywania. Używanie interfejsu API pamięci plików w przypadku innych instancji obiektów Context, np. kontekstu aplikacji, nie gwarantuje prawidłowego działania we wszystkich sytuacjach.
Poniższy fragment kodu pokazuje, jak używać pamięci w środowisku wykonawczym 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" } }
W ramach oddzielnego pamięci wewnętrznej każdego środowiska wykonawczego SDK każdy pakiet SDK ma własny katalog pamięci. Miejsce na dane poszczególnych pakietów SDK to logiczne oddzielenie wewnętrznego miejsca na dane środowiska wykonawczego SDK, które pomaga określić, ile miejsca na dane wykorzystuje każdy pakiet SDK.
Wszystkie interfejsy API pamięci wewnętrznej w obiekcie Context zwracają ścieżkę do pamięci dla każdego pakietu SDK.
Dostęp do identyfikatora wyświetlania reklam udostępnianego przez Usługi Google Play
Jeśli Twój pakiet SDK potrzebuje dostępu do identyfikatora wyświetlania reklam udostępnianego przez Usługi Google Play, użyj metody AdIdManager#getAdId(), aby pobrać wartość asynchronicznie.
Dostęp do identyfikatora zestawu aplikacji udostępnianego przez Usługi Google Play
Jeśli Twój pakiet SDK potrzebuje dostępu do identyfikatora zestawu aplikacji udostępnianego przez Usługi Google Play, użyj
AppSetIdManager#getAppSetId(), aby pobrać wartość asynchronicznie.
Deklarowanie interfejsów API pakietu SDK
Aby pakiet SDK z włączonym środowiskiem wykonawczym był dostępny poza tym środowiskiem, musisz zdefiniować interfejsy API, z których mogą korzystać klienci (pakiet SDK RA lub aplikacja klienta).
Do deklarowania tych interfejsów używaj adnotacji.
Adnotacje
Interfejsy API pakietu SDK muszą być zadeklarowane w języku Kotlin jako interfejsy i klasy danych z użyciem tych adnotacji:
| Adnotacje | |
|---|---|
@PrivacySandboxService |
|
@PrivacySandboxInterface |
|
@PrivacySandboxValue |
|
@PrivacySandboxCallback |
|
Te interfejsy i klasy musisz zdefiniować w dowolnym miejscu w module biblioteki reklamowej z włączonym środowiskiem wykonawczym.
Sposób użycia tych adnotacji znajdziesz w sekcjach poniżej.
@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() }
Typy obsługiwane
Interfejsy API pakietu SDK używanego w czasie działania aplikacji obsługują te typy:
- Wszystkie typy proste w języku programowania Java (takie jak int, long, char, boolean itp.)
- Ciąg znaków
- Interfejsy Kotlin z adnotacjami
@PrivacySandboxInterfacelub@PrivacySandboxCallback - Klasy danych Kotlin z adnotacją
@PrivacySandboxValue - java.lang.List – wszystkie elementy na liście muszą być jednego z obsługiwanych typów danych.
Istnieją dodatkowe zastrzeżenia:
- Klasy danych oznaczone symbolem
@PrivacySandboxValuenie mogą zawierać pól typu@PrivacySandboxCallback - Typy zwracane nie mogą zawierać typów oznaczonych adnotacją
@PrivacySandboxCallback. - Lista nie może zawierać elementów typów oznaczonych adnotacją
@PrivacySandboxInterfacelub@PrivacySandboxCallback
Asynchroniczne interfejsy API
Interfejsy API pakietu SDK zawsze wywołują oddzielny proces, dlatego musimy zadbać o to, aby te wywołania nie blokowały wątku wywołującego klienta.
Aby to osiągnąć, wszystkie metody w interfejsach oznaczonych adnotacjami @PrivacySandboxService, @PrivacySandboxInterface i @PrivacySandboxCallback muszą być wyraźnie zadeklarowane jako asynchroniczne interfejsy API.
Asynchroniczne interfejsy API można zaimplementować w Kotlinie na 2 sposoby:
- Użyj funkcji zawieszania.
- Akceptuj wywołania zwrotne, które są powiadamiane o zakończeniu operacji lub o innych zdarzeniach podczas jej wykonywania. Typ zwracany funkcji musi być jednostką.
Wyjątki
Interfejsy API pakietu SDK nie obsługują żadnych wyjątków sprawdzanych.
Wygenerowany kod pośredniczący przechwytuje wszelkie wyjątki środowiska wykonawczego zgłaszane przez pakiet SDK i przekazuje je jako PrivacySandboxException do klienta wraz z informacjami o przyczynie.
Biblioteka interfejsu
Jeśli masz interfejsy reprezentujące reklamy, np. baner, musisz też wdrożyć interfejs SandboxedUiAdapter, aby umożliwić otwieranie sesji w przypadku załadowanej reklamy.
Te sesje tworzą kanał boczny między klientem a pakietem SDK i służą do 2 głównych celów:
- Otrzymuj powiadomienia o każdej zmianie interfejsu.
- powiadamiać klienta o wszelkich zmianach w sposobie prezentacji interfejsu;
Klient może używać interfejsu oznaczonego adnotacją @PrivacySandboxService do komunikacji z pakietem SDK, więc do tego interfejsu można dodawać dowolne interfejsy API do wczytywania reklam.
Gdy klient poprosi o wczytanie reklamy, wczytaj ją i zwróć instancję interfejsu implementującego SandboxedUiAdapter. Dzięki temu klient może poprosić o otwarcie sesji dla tej reklamy.
Gdy klient poprosi o otwarcie sesji, pakiet SDK z włączonym środowiskiem wykonawczym może utworzyć widok reklamy na podstawie odpowiedzi na żądanie reklamy i dostarczonego kontekstu.
Aby to zrobić, utwórz klasę, która implementuje interfejs SandboxedUiAdapter.Session, i gdy wywoływana jest funkcja SandboxedUiAdapter.openSession(), wywołaj funkcję client.onSessionOpened(), przekazując jako parametr instancję klasy 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)
}
}
}
Ta klasa otrzymuje też powiadomienia o każdej zmianie interfejsu. Możesz użyć tej klasy, aby zmienić rozmiar reklamy lub dowiedzieć się, kiedy zmieniła się konfiguracja.
Więcej informacji o interfejsach API prezentacji interfejsu w środowisku wykonawczym
Pomoc dotycząca aktywności
Aby uruchamiać działania należące do pakietu SDK z Piaskownicy prywatności, musisz zmodyfikować interfejs API pakietu SDK, aby odbierał obiekt SdkActivityLauncher, który jest też udostępniany przez bibliotekę interfejsu.
Na przykład ten interfejs API pakietu SDK powinien uruchamiać działania, więc oczekuje parametru SdkActivityLauncher:
@PrivacySandboxInterface
interface FullscreenAd {
suspend fun show(activityLauncher: SdkActivityLauncher)
}
Punkt wejścia pakietu SDK
Klasa abstrakcyjna SandboxedSdkProvider
zawiera interfejs API, którego środowisko wykonawcze pakietu SDK używa do interakcji z załadowanymi do niego pakietami SDK.
Pakiet SDK używany w czasie działania aplikacji musi implementować tę klasę abstrakcyjną, aby generować punkt wejścia dla środowiska wykonawczego pakietu SDK, które będzie mogło się z nim komunikować.
Aby zapewnić zgodność wsteczną, wprowadziliśmy te klasy:
SandboxedSdkProviderAdapter, który rozszerzaSandboxedSdkProvideri obsługuje żądania wczytywania pakietu SDK niezależnie od dostępności środowiska wykonawczego SDK. Jest on używany wewnętrznie i zadeklarowany w module ASB.SandboxedSdkProviderCompat, klasa abstrakcyjna, która naśladuje interfejsSandboxedSdkProvider.
Więcej informacji o kompatybilności wstecznej środowiska wykonawczego SDK
Narzędzia do generowania warstwy pośredniej dodają kolejną warstwę abstrakcji: generują klasę abstrakcyjną o nazwie AbstractSandboxedSdkProvider na podstawie interfejsu, który został oznaczony adnotacją @PrivacySandboxService.
Ta klasa rozszerza klasę SandboxedSdkProviderCompat i znajduje się w tym samym pakiecie co interfejs z adnotacjami.
// Auto-generated code.
abstract class AbstractSandboxedSdkProvider : SandboxedSdkProviderCompat {
abstract fun createMySdk(context: Context): MySdk
}
Wygenerowana klasa udostępnia jedną abstrakcyjną metodę fabryczną, która przyjmuje Context i oczekuje zwrócenia interfejsu z adnotacją punktu wejścia.
Ta metoda jest nazwana na podstawie interfejsu @PrivacySandboxService, a jej nazwa zaczyna się od create. Jeśli na przykład interfejs ma nazwę MySdk, narzędzia wygenerują createMySdk.
Aby w pełni połączyć punkt wejścia, musisz w pakiecie SDK z włączonym środowiskiem wykonawczym podać implementację @PrivacySandboxService interfejsu z adnotacjami do wygenerowanego AbstractSandboxedSdkProvider.
class MySdkSandboxedSdkProvider : AbstractSandboxedSdkProvider() {
override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
}
Zmiany w module ASB
W polu compatSdkProviderClassName pliku build.gradle modułu ASB musisz zadeklarować pełną i jednoznaczną nazwę klasy implementacji interfejsu SandboxedSdkProviderCompat.
Jest to klasa zaimplementowana w poprzednim kroku. Plik build.gradle w module ASB należy zmodyfikować w ten sposób:
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"
}
Krok 2. Skonfiguruj środowisko programistyczne Krok 4. Użyj pakietu SDK z włączonym środowiskiem wykonawczym