Créer et utiliser un SDK compatible avec l'environnement d'exécution

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

Créer un SDK compatible avec l'environnement d'exécution

Pour créer un SDK compatible avec l'environnement d'exécution, vous devez suivre les étapes ci-dessous:

  1. Configurer la structure de votre projet
  2. Préparer les dépendances du projet et du module
  3. Ajouter la logique métier de votre SDK
  4. Définir les API du SDK
  5. Spécifier un point d'entrée pour votre SDK

Configurer la structure de votre projet

Nous vous recommandons d'organiser votre projet selon les modules suivants:

  1. Module d'application : l'application de test que vous utilisez pour tester et développer votre SDK, qui représente ce que les clients de votre application réelle auraient. Votre application doit Elles dépendent du module de bibliothèque d'annonces existant (SDK compatible avec l'environnement d'exécution).
  2. Module de bibliothèque d'annonces existant (SDK compatible avec l'environnement d'exécution) : module de bibliothèque Android. contenant l'état "non-runtime-enabled" la logique du SDK, une approche statique SDK associé.
    • Pour commencer, il est possible de diviser les fonctionnalités. Par exemple, du code peut être gérés par votre SDK existant, et certains peuvent être acheminés vers l'instance SDK.
  3. Module de bibliothèque d'annonces compatible avec l'environnement d'exécution : contient votre SDK compatible avec l'environnement d'exécution. la logique métier. Il peut être créé sur Android Studio en tant que bibliothèque Android de ce module.
  4. Runtime-enabled ASB module (Module ASB compatible avec l'environnement d'exécution) : définit les données du package pour le regroupement un code de SDK compatible avec l'environnement d'exécution dans un ASB.
    • Il doit être créé manuellement à l'aide du com.android.privacy-sandbox-sdk. Pour ce faire, créez dans un nouveau répertoire.
    • Ce module ne doit contenir aucun code et juste un fichier build.gradle vide avec les dépendances de votre module de bibliothèque d'annonces compatible avec l'environnement d'exécution. Le contenu de ce fichier est défini dans Préparez votre SDK.
    • N'oubliez pas d'inclure ce module dans le fichier settings.gradle, ainsi que dans la section module existant de la bibliothèque d'annonces.

La structure de projet de ce guide est une suggestion, vous pouvez choisir une autre pour votre SDK et appliquez les mêmes principes techniques. Vous pouvez toujours créer d'autres modules pour modulariser le code de l'application et des modules de la bibliothèque.

Préparer votre SDK

Pour préparer votre projet au développement de SDK compatibles avec l'environnement d'exécution, vous devez : commencez par définir des dépendances d'outils et de bibliothèques:

  • Les bibliothèques de rétrocompatibilité du SDK Runtime, qui prennent en charge appareils non équipés de la Privacy Sandbox (Android 13 et versions antérieures) (androidx.privacysandbox.sdkruntime:)
  • Bibliothèques d'UI pour prendre en charge la présentation des annonces (androidx.privacysandbox.ui:)
  • Outils pour les développeurs de SDK compatibles avec la déclaration de l'API du SDK et la génération de shim (androidx.privacysandbox.tools:)
  1. Ajoutez cet indicateur au fichier gradle.properties de votre projet pour permettre de créer des SDK compatibles avec l'environnement d'exécution.

    # This enables the Privacy Sandbox for your project on Android Studio.
    android.experimental.privacysandboxsdk.enable=true
    android.experimental.privacysandboxsdk.requireServices=false
    
  2. Modifiez le fichier build.gradle de votre projet pour inclure les bibliothèques d'aide Jetpack et d'autres dépendances:

    // 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. Mettez à jour le fichier build.gradle dans le module de la bibliothèque d'annonces compatibles avec l'environnement d'exécution (SDK RE) pour inclure ces dépendances.

    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. Remplacez le fichier build.gradle dans votre module ASB compatible avec l'environnement d'exécution par le code suivant:

    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. Mettez à jour le fichier build.gradle dans votre module de bibliothèque d'annonces existante (SDK RA) pour inclure les dépendances suivantes:

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

Ajouter une logique métier pour le SDK

Implémentez la logique métier de votre SDK comme vous le feriez d'habitude dans le de la bibliothèque d'annonces compatible avec l'environnement d'exécution.

Si vous migrez un SDK existant, déplacez autant de logiques métier, d'interface et de fonctions système que vous le souhaitez à ce stade, mais tenez compte d'une migration complète à l'avenir.

Si vous avez besoin d'accéder à de l'espace de stockage, à l'identifiant publicitaire Google Play ou à l'ID du groupe d'applications, lisez les sections suivantes:

Utiliser des API de stockage dans votre SDK

Les SDK du SDK Runtime ne peuvent plus accéder, lire ni écrire dans la mémoire de stockage interne d'une application et inversement.

Le SDK Runtime dispose de sa propre zone de stockage interne, distincte de l'application.

Les SDK peuvent accéder à cette mémoire de stockage interne distincte à l'aide des API de stockage de fichiers sur l'objet Context renvoyé par SandboxedSdkProvider#getContext().

Les SDK ne peuvent utiliser que la mémoire de stockage interne. Seules les API de stockage interne, comme Context.getFilesDir() ou Context.getCacheDir(). Voir d'autres exemples dans Accès depuis la mémoire de stockage interne.

L'accès au stockage externe à partir du SDK Runtime n'est pas possible. L'appel d'API pour accéder au stockage externe génère une exception ou renvoie une valeur nulle. Voici quelques exemples:

Vous devez utiliser le Context renvoyé par SandboxedSdkProvider.getContext() pour le stockage. Il n'est pas garanti que l'utilisation de l'API de stockage de fichiers sur une autre instance d'objet Context, telle que le contexte de l'application, fonctionne comme prévu dans toutes les situations.

L'extrait de code suivant montre comment utiliser le stockage dans le 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"
    }
}

Dans la mémoire de stockage interne distincte de chaque SDK Runtime, chaque SDK possède son propre répertoire de stockage. Le stockage par SDK est une séparation logique de la mémoire de stockage interne du SDK Runtime, qui permet de prendre en compte la quantité d'espace de stockage utilisée par chaque SDK.

Toutes les API de stockage interne de l'objet Context renvoient un chemin de stockage pour chaque SDK.

Accéder à l'identifiant publicitaire fourni par les services Google Play

Si votre SDK a besoin d'accéder à l'identifiant publicitaire fourni par les services Google Play, utilisez AdIdManager#getAdId() pour récupérer la valeur de manière asynchrone.

<ph type="x-smartling-placeholder">

Accéder à l'ID du groupe d'applications fourni par les services Google Play

Si votre SDK a besoin d'accéder à l'ID du groupe d'applications fourni par les services Google Play, utilisez AppSetIdManager#getAppSetId() pour récupérer la valeur de manière asynchrone.

Déclarer les API du SDK

Pour que votre SDK compatible avec l'environnement d'exécution soit accessible en dehors de l'environnement d'exécution, vous devez pour définir les API que les clients (SDK RA ou application cliente) peuvent utiliser.

Utilisez des annotations pour déclarer ces interfaces.

Annotations

Les API du SDK doivent être déclarées en Kotlin en tant qu'interfaces et classes de données à l'aide de la méthode les annotations suivantes:

Annotations
@PrivacySandboxService
  • Définit le point d'entrée de votre SDK compatible avec l'environnement d'exécution.
  • Doit être unique
@PrivacySandboxInterface
  • Permet une modularisation plus poussée et l'exposition des interfaces
  • Peut avoir plusieurs instances
@PrivacySandboxValue
  • Permet d'envoyer des données entre les processus
  • Semblables aux structures immuables, qui peuvent renvoyer plusieurs valeurs de types différents.
@PrivacySandboxCallback
  • Déclare les API avec un rappel.
  • Fournit un canal secondaire pour appeler le code client

Vous devez définir ces interfaces et classes n'importe où dans le de la bibliothèque d'annonces compatible avec l'environnement d'exécution.

Pour en savoir plus sur l'utilisation de ces annotations, consultez les sections suivantes.

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

Types acceptés

Les API SDK compatibles avec l'environnement d'exécution sont compatibles avec les types suivants:

  • Tous les types primitifs dans le langage de programmation Java (tels que int, long, char, booléen, etc.)
  • Chaîne
  • Les interfaces Kotlin annotées avec @PrivacySandboxInterface ou @PrivacySandboxCallback
  • Classes de données Kotlin annotées avec @PrivacySandboxValue
  • java.lang.List - Tous les éléments de la liste doivent correspondre à l'une des données prises en charge Types

Mises en garde supplémentaires :

  • Les classes de données annotées avec @PrivacySandboxValue ne peuvent pas contenir de champs de Type : @PrivacySandboxCallback
  • Les types renvoyés ne peuvent pas contenir de types annotés avec @PrivacySandboxCallback
  • La liste ne peut pas contenir d'éléments de types annotés avec @PrivacySandboxInterface ou @PrivacySandboxCallback

API asynchrones

Étant donné que les API du SDK appellent toujours un processus distinct, nous devons vous assurer que ces appels ne bloquent pas le thread appelant du client.

Pour ce faire, toutes les méthodes des interfaces annotées avec @PrivacySandboxService, @PrivacySandboxInterface et @PrivacySandboxCallback doivent être déclarées explicitement en tant qu'API asynchrones.

Les API asynchrones peuvent être implémentées en Kotlin de deux manières:

  1. Utilisez des fonctions de suspension.
  2. accepter les rappels qui sont avertis lorsque l'opération est terminée ; d'autres événements pendant la progression de l'opération. Type renvoyé doit être une unité.

Exceptions

Les API du SDK ne sont compatibles avec aucune forme d'exception vérifiée.

Le code shim généré détecte toutes les exceptions d'exécution générées par le SDK les envoie au client en tant que PrivacySandboxException avec des informations sur la cause en elle-même.

Bibliothèque d'UI

Si certaines de vos interfaces représentent des annonces (une bannière, par exemple), vous devez également implémenter l'interface SandboxedUiAdapter afin d'activer l'ouverture de sessions pour l'annonce chargée.

Ces sessions constituent un canal secondaire entre le client et le SDK, et elles qui remplissent deux objectifs principaux:

  • Recevez des notifications en cas de modification de l'interface utilisateur.
  • Informer le client de toute modification de la présentation de l'interface utilisateur

Comme le client peut utiliser l'interface annotée avec @PrivacySandboxService pour communiquer avec votre SDK, vous pouvez ajouter à cette de commande.

Lorsque le client demande à charger une annonce, chargez l'annonce et renvoyez une instance de l'interface implémentant SandboxedUiAdapter. Cela permet au client de demander des sessions d'ouverture pour cette annonce.

Lorsque le client demande à ouvrir une session, votre SDK compatible avec l'environnement d'exécution peut créer un visionnage d'annonce à l'aide de la réponse d'annonce et du contexte fourni.

Pour ce faire, créez une classe qui implémente l'interface SandboxedUiAdapter.Session et, lorsque SandboxedUiAdapter.openSession() est appelé, veillez à appeler client.onSessionOpened(), en transmettant une instance de la classe Session en tant que paramètre.

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

Cette classe reçoit également des notifications chaque fois qu'une modification de l'interface utilisateur se produit. Vous pouvez utilisez cette classe pour redimensionner l'annonce ou pour savoir quand la configuration a changé, par exemple.

En savoir plus sur les API de présentation de l'interface utilisateur dans l'environnement d'exécution

Prise en charge des activités

Pour démarrer les activités appartenant au SDK à partir de la Privacy Sandbox, vous devez modifier l'API du SDK afin de recevoir un objet SdkActivityLauncher, également fourni par la bibliothèque d'UI.

Par exemple, l'API SDK suivante doit lancer des activités et attend donc le paramètre SdkActivityLauncher:

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

Point d'entrée du SDK

La classe abstraite SandboxedSdkProvider encapsule l'API que le SDK Runtime utilise pour interagir avec les SDK qui y sont chargés.

Un SDK compatible avec l'environnement d'exécution doit implémenter cette classe abstraite afin de générer un point d'entrée permettant au SDK Runtime de communiquer avec elle.

Pour assurer la rétrocompatibilité, nous avons introduit les classes suivantes:

En savoir plus sur la rétrocompatibilité pour le SDK Runtime

Les outils de génération de shims ajoutent une couche d'abstraction supplémentaire: ils génèrent une classe abstraite appelée AbstractSandboxedSdkProvider à l'aide de l'interface que vous avez annotée avec @PrivacySandboxService.

Cette classe étend SandboxedSdkProviderCompat et se trouve sous le même package que votre interface annotée.

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

Cette classe générée expose une seule méthode de fabrique abstraite qui accepte un Context et s'attend à ce que votre interface annotée de point d'entrée soit renvoyée.

Cette méthode porte le nom de votre interface @PrivacySandboxService, en ajoutant le préfixe create au nom. Par exemple, si votre interface s'appelle MySdk, les outils générer createMySdk.

Pour connecter complètement votre point d'entrée, vous devez fournir une implémentation de votre interface annotée @PrivacySandboxService dans le SDK compatible avec l'environnement d'exécution dans le AbstractSandboxedSdkProvider généré.

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

Modifications apportées au module ASB

Vous devez déclarer le nom de classe complet de votre implémentation de SandboxedSdkProviderCompat dans le champ compatSdkProviderClassName du build.gradle de votre module ASB.

Il s'agit de la classe que vous avez implémentée à l'étape précédente, et vous devez modifier le fichier build.gradle sur votre module ASB comme suit:

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

Étape 2: Configurez votre environnement de développement Étape 4: Utilisez le SDK compatible avec l'environnement d'exécution