Criar e consumir um SDK ativado pelo ambiente de execução

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

Criar um SDK ativado pelo ambiente de execução

Conclua as etapas a seguir para criar um SDK ativado pelo ambiente de execução:

  1. Configurar a estrutura do projeto
  2. Preparar o projeto e as dependências do módulo
  3. Adicionar a lógica de negócios do SDK
  4. Definir as APIs do SDK
  5. Especificar um ponto de entrada para o SDK

Configurar a estrutura do projeto

Recomendamos que seu projeto seja organizado nos seguintes módulos:

  1. Módulo do app: o app de teste que você está usando para testar e desenvolver seu , representando o que os clientes reais do seu app teriam. Seu app precisa dependem do módulo da biblioteca de anúncios (SDK baseado no ambiente de execução).
  2. Módulo de biblioteca de anúncios existente (SDK com reconhecimento de ambiente de execução): um módulo de biblioteca do Android. que contêm o arquivo "não ativado pelo ambiente de execução" A lógica do SDK, uma maneira estática SDK vinculado.
    • Para começar, os recursos podem ser divididos. Por exemplo, alguns códigos podem ser processados pelo SDK existente, e alguns podem ser roteados para o SDK do Vertex AI Pipelines.
  3. Módulo da biblioteca de anúncios ativado pelo ambiente de execução: contém seu SDK ativado pelo ambiente de execução. lógica de negócios. Isso pode ser criado no Android Studio como uma biblioteca Android. mais tarde neste módulo.
  4. Módulo ASB ativado pelo ambiente de execução: define os dados do pacote para agrupar os código do SDK ativado pelo ambiente de execução em um ASB.
    • Ela precisa ser criada manualmente usando o com.android.privacy-sandbox-sdk. Você pode fazer isso criando um novo diretório.
    • Esse módulo não pode conter nenhum código e apenas um build.gradle vazio com dependências do módulo da biblioteca de anúncios ativada pelo ambiente de execução. O conteúdo desse arquivo é definido em Prepare o SDK.
    • Lembre-se de incluir esse módulo no arquivo settings.gradle e no módulo biblioteca de anúncios existente.

A estrutura do projeto neste guia é uma sugestão. Você pode escolher uma do SDK e aplicam os mesmos princípios técnicos. Você sempre pode criar outros módulos adicionais para modularizar o código no app e nos módulos da biblioteca.

Preparar o SDK

Para preparar seu projeto para o desenvolvimento de SDKs ativados pelo ambiente de execução, você precisa: primeiro defina algumas dependências de ferramentas e bibliotecas:

  • Bibliotecas de compatibilidade com versões anteriores do SDK Runtime, que oferecem suporte a Dispositivos que não têm o Sandbox de privacidade (Android 13 e versões anteriores) (androidx.privacysandbox.sdkruntime:)
  • Bibliotecas de interface para oferecer suporte à apresentação de anúncios (androidx.privacysandbox.ui:)
  • Ferramentas para desenvolvedores do SDK compatíveis com a declaração da API do SDK e a geração de shim (androidx.privacysandbox.tools:)
  1. Adicione essa flag ao arquivo gradle.properties do projeto para ativar o recurso de criação de SDKs ativados pelo ambiente de execução.

    # This enables the Privacy Sandbox for your project on Android Studio.
    android.experimental.privacysandboxsdk.enable=true
    android.experimental.privacysandboxsdk.requireServices=false
    
  2. Modifique o build.gradle do projeto para incluir as bibliotecas auxiliares do Jetpack e outras dependências:

    // 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. Atualize o arquivo build.gradle no módulo da biblioteca de anúncios ativada pelo ambiente de execução (SDK RE) para incluir essas dependências.

    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. Substitua o arquivo build.gradle no módulo ASB ativado pelo ambiente de execução pelo seguinte:

    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. Atualize o arquivo build.gradle no módulo da biblioteca de anúncios existente (SDK da RA) para incluir estas dependências:

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

Adicionar lógica de negócios do SDK

Implemente a lógica de negócios do SDK como faria regularmente no módulo da biblioteca de anúncios ativada pelo tempo de execução.

Se você estiver migrando um SDK, migre o máximo possível da lógica de negócios, da interface e das funções voltadas ao sistema que quiser nessa etapa, mas considere uma migração completa no futuro.

Se você precisar de acesso ao armazenamento, ao ID de publicidade do Google Play ou ao ID do conjunto de apps, leia as seguintes seções:

Usar APIs de armazenamento no SDK

Os SDKs no SDK Runtime não podem mais acessar, ler nem gravar no armazenamento interno de um app. e vice-versa.

O SDK Runtime recebe uma área de armazenamento interno separada do app.

Os SDKs podem acessar esse armazenamento interno separado usando as APIs de armazenamento de arquivos no objeto Context retornado pelo SandboxedSdkProvider#getContext().

Os SDKs só podem usar armazenamento interno, portanto, apenas APIs de armazenamento interno, como Context.getFilesDir() ou Context.getCacheDir() trabalho. Confira mais exemplos em Acessar do armazenamento interno.

Não há suporte para acesso ao armazenamento externo pelo SDK Runtime. Chamar APIs para acessar o armazenamento externo vai gerar uma exceção ou retornar um valor nulo. A lista a seguir inclui alguns exemplos:

Use o Context retornado por SandboxedSdkProvider.getContext() para armazenamento. Não há garantia de que o uso da API de armazenamento de arquivos em qualquer outra instância de objeto Context, como o contexto do aplicativo, vai funcionar como esperado em todas as situações.

O snippet de código a seguir demonstra como usar o armazenamento no 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"
    }
}

No armazenamento interno separado de cada SDK Runtime, cada SDK tem o próprio diretório de armazenamento. Essa é uma separação lógica do armazenamento interno do SDK Runtime, que ajuda a contabilizar a quantidade de armazenamento usada por um SDK.

Todas as APIs de armazenamento interno no objeto Context retornam um caminho de armazenamento para cada SDK.

Acessar o ID de publicidade fornecido pelo Google Play Services

Se o SDK precisar acessar o ID de publicidade fornecido pelo Google Play Services, use AdIdManager#getAdId() para recuperar o valor de forma assíncrona.

Acessar o ID do conjunto de apps fornecido pelo Google Play Services

Se o SDK precisar de acesso ao ID do conjunto de apps fornecido pelo Google Play Services, use AppSetIdManager#getAppSetId() para recuperar o valor de forma assíncrona.

Declarar APIs do SDK

Para que o SDK ativado pelo ambiente de execução possa ser acessado fora dele, você precisa para definir APIs que os clientes (SDK RA ou aplicativo cliente) podem consumir.

Use anotações para declarar essas interfaces.

Anotações

As APIs do SDK precisam ser declaradas em Kotlin como interfaces e classes de dados usando a seguintes anotações:

Anotações
@PrivacySandboxService
  • Define o entry-point do SDK RE.
  • Precisa ser exclusivo
@PrivacySandboxInterface
  • Permite mais modularização e exposição de interfaces.
  • Pode ter várias instâncias
@PrivacySandboxValue
  • Permite o envio de dados entre processos.
  • Semelhantes aos structs imutáveis, que podem retornar diversos valores de tipos diferentes
@PrivacySandboxCallback
  • Declara APIs com um callback
  • Fornece um canal de retorno para invocar o código do cliente.

Você precisa definir essas interfaces e classes em qualquer lugar da módulo da biblioteca de anúncios ativada pelo tempo de execução.

Consulte o uso dessas anotações nas seções a seguir.

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

Tipos compatíveis

As APIs do SDK ativadas pelo ambiente de execução oferecem suporte aos seguintes tipos:

  • Todos os tipos primitivos da linguagem de programação Java (como int, long, char, booleano e assim por diante)
  • String
  • Interfaces Kotlin anotadas com @PrivacySandboxInterface ou @PrivacySandboxCallback
  • Classes de dados Kotlin com a anotação @PrivacySandboxValue
  • java.lang.List - todos os elementos da Lista devem ser um dos dados suportados tipos

Há algumas ressalvas adicionais:

  • As classes de dados anotadas com @PrivacySandboxValue não podem conter campos de tipo @PrivacySandboxCallback
  • Os tipos de retorno não podem conter tipos anotados com @PrivacySandboxCallback
  • A lista não pode conter elementos de tipos anotados com @PrivacySandboxInterface ou @PrivacySandboxCallback

APIs assíncronas

Como as APIs do SDK sempre fazem uma chamada para um processo separado, precisamos verifique se essas chamadas não bloqueiam o thread de chamada do cliente.

Para isso, todos os métodos nas interfaces anotadas com @PrivacySandboxService, @PrivacySandboxInterface e @PrivacySandboxCallback precisam ser declaradas explicitamente como APIs assíncronas.

APIs assíncronas podem ser implementadas no Kotlin de duas maneiras:

  1. Use funções de suspensão.
  2. Aceita retornos de chamada que são notificados quando a operação é concluída ou de outros eventos durante o andamento da operação. O tipo de retorno do precisa ser uma unidade.
.

Exceções

As APIs do SDK não oferecem suporte a nenhuma forma de exceção verificada.

O código paliativo gerado captura todas as exceções de tempo de execução geradas pelo SDK e e as envia como PrivacySandboxException para o cliente com informações sobre a causa envolvida nele.

Biblioteca de interface

Se você tiver interfaces que representam anúncios, como um banner, também será necessário implementar a interface SandboxedUiAdapter para ativar as sessões de abertura do anúncio carregado.

Essas sessões formam um canal secundário entre o cliente e o SDK e atendem a dois propósitos principais:

  • Receba notificações sempre que ocorrer uma mudança na interface.
  • Notificar o cliente sobre qualquer mudança na apresentação da interface.
.

Como o cliente pode usar a interface com a anotação @PrivacySandboxService para se comunicar com o SDK, todas as APIs para carregamento de anúncios podem ser adicionadas ao interface gráfica do usuário.

Quando o cliente solicitar o carregamento de um anúncio, carregue o anúncio e retorne uma instância do a interface que implementa SandboxedUiAdapter. Isso permite que o cliente solicite sessões de abertura para esse anúncio.

Quando o cliente solicita a abertura de uma sessão, o SDK ativado pelo ambiente de execução pode criar uma visualização de anúncio usando a resposta do anúncio e o contexto fornecido.

Para isso, crie uma classe que implemente a interface SandboxedUiAdapter.Session e, quando SandboxedUiAdapter.openSession() for chamado, chame client.onSessionOpened(), transmitindo uma instância da classe Session como um parâmetro.

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

Essa classe também recebe notificações sempre que ocorre uma mudança na interface. Você pode use essa classe para redimensionar o anúncio ou saber quando a configuração mudou, por exemplo.

Saiba mais sobre as APIs de apresentação da interface no ambiente de execução.

Suporte de atividade

Para iniciar as atividades do SDK no Sandbox de privacidade, modifique a API do SDK para receber um objeto SdkActivityLauncher, também fornecido pela biblioteca da interface.

Por exemplo, a API do SDK a seguir precisa iniciar atividades, então ela espera o parâmetro SdkActivityLauncher:

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

Ponto de entrada do SDK

A classe abstrata SandboxedSdkProvider encapsula a API que o SDK Runtime usa para interagir com os SDKs carregados nele.

Um SDK ativado pelo ambiente de execução precisa implementar essa classe abstrata para gerar um ponto de entrada ao SDK Runtime e se comunicar com ele.

Para oferecer suporte à compatibilidade com versões anteriores, introduzimos as seguintes classes:

Saiba mais sobre a compatibilidade com versões anteriores do SDK Runtime.

As ferramentas de geração de paliativos adicionam outra camada de abstração: elas geram uma classe abstrata chamada AbstractSandboxedSdkProvider usando a interface que você anotou com @PrivacySandboxService.

Essa classe estende SandboxedSdkProviderCompat e está no mesmo pacote que a interface com anotação.

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

Essa classe gerada expõe um único método de fábrica abstrato que usa uma Context e espera que sua interface com anotação do ponto de entrada seja retornada.

Esse método é nomeado de acordo com a interface @PrivacySandboxService, incluindo o prefixo create ao nome. Por exemplo, se o nome da interface for MySdk, as ferramentas gerar createMySdk.

Para conectar totalmente seu ponto de entrada, forneça uma implementação da interface com a anotação @PrivacySandboxService no SDK ativado pelo ambiente de execução para o AbstractSandboxedSdkProvider gerado.

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

Mudanças no módulo ASB

É necessário declarar o nome de classe totalmente qualificado da implementação do SandboxedSdkProviderCompat no campo compatSdkProviderClassName do build.gradle do módulo ASB.

Essa é a classe que você implementou na etapa anterior, e você modificaria o build.gradle no módulo ASB da seguinte maneira:

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

Etapa 2: configurar o ambiente de desenvolvimento Etapa 4: consumir o SDK ativado pelo ambiente de execução