Crea e utilizza un SDK abilitato per il runtime

1
Key concepts
2
Set up your development environment
3
Build an RE SDK
4
Consume the RE SDK
5
Testing, and building for distribution

Crea un SDK abilitato per il runtime

Per creare un SDK abilitato per il runtime, devi completare i seguenti passaggi:

  1. Configurare la struttura del progetto
  2. Prepara le dipendenze del progetto e del modulo
  3. Aggiungere la logica di business dell'SDK
  4. Definisci le API SDK
  5. Specificare un punto di ingresso per l'SDK

Configurare la struttura del progetto

Ti consigliamo di organizzare il progetto nei seguenti moduli:

  1. Modulo app: l'app di test che utilizzi per testare e sviluppare il tuo SDK, che rappresenta ciò che avrebbero i tuoi client app reali. La tua app deve avere una dipendenza dal modulo della libreria di annunci esistente (SDK runtime-aware).
  2. Modulo della libreria di annunci esistente (SDK runtime-aware): un modulo della libreria Android contenente la logica dell'SDK "non runtime-enabled" esistente, un SDK collegato staticamente.
    • Per iniziare, le funzionalità possono essere suddivise. Ad esempio, parte del codice può essere gestita dall'SDK esistente e parte può essere indirizzata all'SDK abilitato per il runtime.
  3. Modulo della libreria di annunci abilitata per il runtime: contiene la logica di business dell'SDK abilitato per il runtime. Può essere creato su Android Studio come modulo della libreria Android.
  4. Modulo ASB abilitato per il runtime: definisce i dati del pacchetto per raggruppare il codice SDK abilitato per il runtime in un ASB.
    • Deve essere creato manualmente utilizzando il tipo com.android.privacy-sandbox-sdk. Per farlo, puoi creare una nuova directory.
    • Questo modulo non deve contenere codice, ma solo un file build.gradle vuoto con le dipendenze dal modulo della libreria di annunci abilitata per l'esecuzione. Il contenuto di questo file è definito in Prepara l'SDK.
    • Ricorda di includere questo modulo nel file settings.gradle e nel modulo della libreria pubblicitaria esistente.

La struttura del progetto in questa guida è un suggerimento. Puoi scegliere una struttura diversa per il tuo SDK e applicare gli stessi principi tecnici. Puoi sempre creare altri moduli aggiuntivi per modularizzare il codice nell'app e nei moduli della libreria.

Prepara l'SDK

Per preparare il progetto per lo sviluppo di SDK abilitati al runtime, devi prima definire alcune dipendenze di librerie e strumenti:

  • Librerie di compatibilità con le versioni precedenti di SDK Runtime, che forniscono supporto per i dispositivi che non dispongono di Privacy Sandbox (Android 13 e versioni precedenti) (androidx.privacysandbox.sdkruntime:)
  • Librerie UI per supportare la presentazione degli annunci (androidx.privacysandbox.ui:)
  • Strumenti per sviluppatori di SDK per supportare la dichiarazione dell'API SDK e la generazione di shim (androidx.privacysandbox.tools:)
  1. Aggiungi questo flag al file gradle.properties del progetto per attivare la funzionalità di creazione di SDK abilitati per il runtime.

    # This enables the Privacy Sandbox for your project on Android Studio.
    android.experimental.privacysandboxsdk.enable=true
    android.experimental.privacysandboxsdk.requireServices=false
    
  2. Modifica il file build.gradle del progetto per includere le librerie Jetpack helper e altre dipendenze:

    // 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
    }
    
  3. Aggiorna il file build.gradle nel modulo della libreria di annunci abilitata per il runtime (SDK RE) per includere queste dipendenze.

    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"
    }
    
  4. Sostituisci il file build.gradle nel tuo modulo ASB abilitato per il runtime con quanto segue:

    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>')
    }
    
  5. Aggiorna il file build.gradle nel modulo della tua libreria di annunci esistente (SDK RA) per includere le seguenti dipendenze:

    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"
    }
    

Aggiungere la logica di business dell'SDK

Implementa la logica di business dell'SDK come faresti normalmente all'interno del modulo della libreria di annunci abilitata per il runtime.

Se hai un SDK esistente di cui stai eseguendo la migrazione, sposta in questa fase la maggior parte della logica di business, dell'interfaccia e delle funzioni rivolte al sistema che vuoi, ma prevedi una migrazione completa in futuro.

Se hai bisogno di accedere allo spazio di archiviazione, all'ID pubblicità Google Play o all'ID set di app, leggi le sezioni seguenti:

Utilizzare le API di archiviazione nell'SDK

Gli SDK in SDK Runtime non possono più accedere, leggere o scrivere nella memoria interna di un'app e viceversa.

A SDK Runtime viene assegnata una propria area di archiviazione interna, separata dall'app.

Gli SDK possono accedere a questo spazio di archiviazione interno separato utilizzando le API di archiviazione dei file sull'oggetto Context restituito da SandboxedSdkProvider#getContext().

Gli SDK possono utilizzare solo la memoria interna, quindi funzionano solo le API di memoria interna, come Context.getFilesDir() o Context.getCacheDir(). Vedi altri esempi in Accesso dalla memoria interna.

L'accesso all'archiviazione esterna da SDK Runtime non è supportato. La chiamata alle API per accedere allo spazio di archiviazione esterno genererà un'eccezione o restituirà un valore nullo. Di seguito sono riportati alcuni esempi:

Devi utilizzare Context restituito da SandboxedSdkProvider.getContext() per lo spazio di archiviazione. L'utilizzo dell'API File Storage su qualsiasi altra istanza dell'oggetto Context, ad esempio il contesto dell'applicazione, non è garantito che funzioni come previsto in tutte le situazioni.

Il seguente snippet di codice mostra come utilizzare l'archiviazione in SDK Runtime:

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"
    }
}

All'interno dello spazio di archiviazione interno separato per ogni SDK Runtime, ogni SDK ha la propria directory di archiviazione. Lo spazio di archiviazione per SDK è una separazione logica dello spazio di archiviazione interno di SDK Runtime che consente di tenere conto della quantità di spazio di archiviazione utilizzata da ciascun SDK.

Tutte le API di archiviazione interna dell'oggetto Context restituiscono un percorso di archiviazione per ogni SDK.

Accedere all'ID pubblicità fornito da Google Play Services

Se il tuo SDK deve accedere all'ID pubblicità fornito da Google Play Services, utilizza AdIdManager#getAdId() per recuperare il valore in modo asincrono.

Accedere all'ID set di app fornito da Google Play Services

Se il tuo SDK deve accedere all'ID set di app fornito da Google Play Services, utilizza AppSetIdManager#getAppSetId() per recuperare il valore in modo asincrono.

Dichiarare le API SDK

Affinché l'SDK abilitato per il runtime sia accessibile al di fuori del runtime, devi definire le API che i client (SDK RA o l'app client) possono utilizzare.

Utilizza le annotazioni per dichiarare queste interfacce.

Annotazioni

Le API SDK devono essere dichiarate in Kotlin come interfacce e classi di dati utilizzando le seguenti annotazioni:

Annotazioni
@PrivacySandboxService
  • Definisce l'entry point dell'SDK RE
  • Deve essere univoco
@PrivacySandboxInterface
  • Consente un'ulteriore modularizzazione e l'esposizione delle interfacce
  • Può avere più istanze
@PrivacySandboxValue
  • Consente l'invio di dati tra processi
  • Simile alle struct immutabili, che possono restituire più valori di tipi diversi
@PrivacySandboxCallback
  • Dichiara API con un callback
  • Fornisce un canale secondario per richiamare il codice client

Devi definire queste interfacce e classi in qualsiasi punto all'interno del modulo della libreria di annunci abilitata all'esecuzione.

Consulta l'utilizzo di queste annotazioni nelle sezioni seguenti.

@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()
}

Tipi supportati

Le API SDK abilitate per il runtime supportano i seguenti tipi:

  • Tutti i tipi primitivi nel linguaggio di programmazione Java (ad esempio int, long, char, boolean e così via)
  • Stringa
  • Interfacce Kotlin annotate con @PrivacySandboxInterface o @PrivacySandboxCallback
  • Classi di dati Kotlin annotate con @PrivacySandboxValue
  • java.lang.List: tutti gli elementi nell'elenco devono essere uno dei tipi di dati supportati

Esistono alcune avvertenze aggiuntive:

  • Le classi di dati annotate con @PrivacySandboxValue non possono contenere campi di tipo @PrivacySandboxCallback
  • I tipi restituiti non possono contenere tipi annotati con @PrivacySandboxCallback
  • L'elenco non può contenere elementi di tipi annotati con @PrivacySandboxInterface o @PrivacySandboxCallback

API asincrone

Poiché le API SDK effettuano sempre una chiamata a un processo separato, dobbiamo assicurarci che queste chiamate non blocchino il thread di chiamata del client.

Per ottenere questo risultato, tutti i metodi nelle interfacce annotate con @PrivacySandboxService, @PrivacySandboxInterface e @PrivacySandboxCallback devono essere dichiarati esplicitamente come API asincrone.

Le API asincrone possono essere implementate in Kotlin in due modi:

  1. Utilizza le funzioni di sospensione.
  2. Accetta i callback che ricevono una notifica al termine dell'operazione o di altri eventi durante l'avanzamento dell'operazione. Il tipo restituito della funzione deve essere un'unità.

Eccezioni

Le API SDK non supportano alcuna forma di eccezioni controllate.

Il codice shim generato rileva eventuali eccezioni di runtime generate dall'SDK e le genera come PrivacySandboxException al client con informazioni sulla causa incluse.

Libreria UI

Se hai interfacce che rappresentano annunci, ad esempio un banner, devi anche implementare l'interfaccia SandboxedUiAdapter per attivare l'apertura delle sessioni per l'annuncio caricato.

Queste sessioni formano un canale secondario tra il client e l'SDK e svolgono due scopi principali:

  • Ricevi notifiche ogni volta che si verifica una modifica dell'interfaccia utente.
  • Comunica al cliente eventuali modifiche alla presentazione dell'interfaccia utente.

Poiché il client può utilizzare l'interfaccia annotata con @PrivacySandboxService per comunicare con il tuo SDK, è possibile aggiungere a questa interfaccia qualsiasi API per il caricamento degli annunci.

Quando il client richiede di caricare un annuncio, carica l'annuncio e restituisce un'istanza dell'interfaccia che implementa SandboxedUiAdapter. In questo modo, il cliente può richiedere l'apertura di sessioni per l'annuncio.

Quando il client richiede di aprire una sessione, l'SDK con runtime abilitato può creare una visualizzazione annuncio utilizzando la risposta all'annuncio e il contesto fornito.

Per farlo, crea una classe che implementi l'interfaccia SandboxedUiAdapter.Session e, quando viene chiamato SandboxedUiAdapter.openSession(), assicurati di chiamare client.onSessionOpened(), passando un'istanza della classe Session come parametro.

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)
       }
   }
}

Questa classe riceve anche notifiche ogni volta che si verifica una modifica della UI. Puoi utilizzare questa classe per ridimensionare l'annuncio o sapere quando la configurazione è cambiata, ad esempio.

Scopri di più sulle API di presentazione dell'interfaccia utente nel runtime.

Supporto per l'attività

Per avviare attività di proprietà dell'SDK da Privacy Sandbox, devi modificare l'API SDK per ricevere un oggetto SdkActivityLauncher, fornito anche dalla libreria UI.

Ad esempio, la seguente API SDK deve avviare le attività, quindi prevede il parametro SdkActivityLauncher:

@PrivacySandboxInterface
interface FullscreenAd {
    suspend fun show(activityLauncher: SdkActivityLauncher)
}

Punto di ingresso dell'SDK

La classe astratta SandboxedSdkProvider incapsula l'API che SDK Runtime utilizza per interagire con gli SDK caricati.

Un SDK abilitato per il runtime deve implementare questa classe astratta per generare un punto di ingresso per il runtime dell'SDK in modo che possa comunicare con esso.

Per il supporto della compatibilità con le versioni precedenti, abbiamo introdotto le seguenti classi:

Scopri di più sulla compatibilità con le versioni precedenti per l'SDK Runtime.

Gli strumenti di generazione di shim aggiungono un altro livello di astrazione: generano una classe astratta chiamata AbstractSandboxedSdkProvider utilizzando l'interfaccia annotata con @PrivacySandboxService.

Questa classe estende SandboxedSdkProviderCompat e si trova nello stesso pacchetto dell'interfaccia annotata.

// Auto-generated code.
abstract class AbstractSandboxedSdkProvider : SandboxedSdkProviderCompat {
    abstract fun createMySdk(context: Context): MySdk
}

Questa classe generata espone un singolo metodo di fabbrica astratto che accetta un Context e prevede che venga restituita l'interfaccia annotata del punto di ingresso.

Questo metodo prende il nome dall'interfaccia @PrivacySandboxService, con l'aggiunta di create. Ad esempio, se la tua interfaccia si chiama MySdk, gli strumenti generano createMySdk.

Per connettere completamente il punto di ingresso, devi fornire un'implementazione dell'interfaccia annotata @PrivacySandboxService nell'SDK abilitato per l'runtime al AbstractSandboxedSdkProvider generato.

class MySdkSandboxedSdkProvider : AbstractSandboxedSdkProvider() {
    override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
}

Modifiche al modulo ASB

Devi dichiarare il nome della classe completo della tua implementazione di SandboxedSdkProviderCompat nel campo compatSdkProviderClassName del file build.gradle del modulo ASB.

Questa è la classe che hai implementato nel passaggio precedente e devi modificare il file build.gradle nel modulo ASB nel seguente modo:

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"
}

Passaggio 2: configura l'ambiente di sviluppo Passaggio 4: utilizza l'SDK abilitato per l'esecuzione