| Key concepts | Set up your development environment | Build an RE SDK | Consume the RE SDK | Testing, and building for distribution |
Criar um SDK ativado pelo ambiente de execução
Você precisa concluir as etapas a seguir para criar um SDK ativado no tempo de execução:
- Configurar a estrutura do projeto
- Preparar as dependências do projeto e do módulo
- Adicionar a lógica de negócios do SDK
- Definir as APIs do SDK
- Especificar um ponto de entrada para o SDK
Configurar a estrutura do projeto
Recomendamos que seu projeto seja organizado nos seguintes módulos:
- Módulo de app: o app de teste que você está usando para testar e desenvolver seu SDK, representando o que seus clientes de app reais teriam. O app precisa ter uma dependência do módulo de biblioteca de anúncios atual (SDK compatível com o tempo de execução).
- Módulo de biblioteca de anúncios atual (SDK compatível com tempo de execução): um módulo de biblioteca do Android
que contém a lógica do SDK "não compatível com tempo de execução" atual, um SDK
vinculado estaticamente.
- Para começar, as funcionalidades podem ser divididas. Por exemplo, parte do código pode ser processada pelo SDK atual, e parte pode ser encaminhada para o SDK ativado pelo ambiente de execução.
- Módulo de biblioteca de anúncios ativado no tempo de execução: contém a lógica de negócios do SDK ativado no tempo de execução. Ele pode ser criado no Android Studio como um módulo de biblioteca do Android.
- Módulo ASB ativado pelo ambiente de execução: define os dados do pacote para agrupar o código do SDK ativado pelo ambiente de execução em um ASB.
- Ele precisa ser criado manualmente usando o tipo com.android.privacy-sandbox-sdk. Para fazer isso, crie um novo diretório.
- Esse módulo não deve conter código, apenas um arquivo build.gradle vazio com dependências do módulo de biblioteca de anúncios ativado no tempo de execução. O conteúdo desse arquivo é definido em Prepare seu SDK.
- Não se esqueça de incluir esse módulo no arquivo settings.gradle e no módulo de biblioteca de anúncios atual.
A estrutura do projeto neste guia é uma sugestão. Você pode escolher uma estrutura diferente para seu SDK e aplicar os mesmos princípios técnicos. Você sempre pode criar outros módulos adicionais para modularizar o código nos módulos de app e de biblioteca.
Preparar o SDK
Para preparar seu projeto para o desenvolvimento de SDKs ativados no tempo de execução, 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 UI para oferecer suporte à apresentação de anúncios (
androidx.privacysandbox.ui:) - Ferramentas para desenvolvedores de SDKs que oferecem suporte à declaração da API do SDK e à geração de shims (
androidx.privacysandbox.tools:)
Adicione essa flag ao arquivo gradle.properties do projeto para ativar a capacidade de criar 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=falseModifique 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 }Atualize o arquivo build.gradle no módulo da biblioteca de anúncios ativada no tempo 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" }Substitua o arquivo build.gradle no seu módulo ASB ativado no tempo 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>') }Atualize o arquivo build.gradle no módulo da biblioteca de anúncios atual (SDK do RA) para incluir as seguintes 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 normalmente no módulo da biblioteca de anúncios ativada para tempo de execução.
Se você estiver migrando um SDK, mova o máximo possível da lógica de negócios, da interface e das funções voltadas para o sistema nesta 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 seções a seguir:
Usar APIs de armazenamento no SDK
Os SDKs no SDK Runtime não podem mais acessar, ler ou gravar no armazenamento interno de um app e vice-versa.
O SDK Runtime recebe uma área de armazenamento interno própria, 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 o armazenamento interno. Portanto, apenas as APIs de armazenamento interno, como Context.getFilesDir() ou
Context.getCacheDir(), funcionam. Confira mais exemplos em
Acessar pelo 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 nulo. Confira alguns exemplos:
- O acesso a arquivos usando o framework de acesso ao armazenamento gera uma SecurityException.
getExternalFilsDir()sempre retorna nulo.
É preciso usar o Context retornado por SandboxedSdkProvider.getContext() para armazenamento. O uso da API de armazenamento de arquivos em qualquer outra instância de objeto Context, como o contexto do app, pode não funcionar conforme o 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 de acesso ao 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 acessar o 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 no tempo de execução seja acessível fora dele, você precisa definir APIs que os clientes (SDK do RA ou app cliente) possam 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 as seguintes anotações:
| Anotações | |
|---|---|
@PrivacySandboxService |
|
@PrivacySandboxInterface |
|
@PrivacySandboxValue |
|
@PrivacySandboxCallback |
|
Você precisa definir essas interfaces e classes em qualquer lugar dentro do módulo da biblioteca de anúncios ativada no 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 no tempo de execução são compatíveis com os seguintes tipos:
- Todos os tipos primitivos na linguagem de programação Java (como int, long, char, boolean etc.)
- String
- Interfaces do Kotlin com anotação
@PrivacySandboxInterfaceou@PrivacySandboxCallback - Classes de dados Kotlin anotadas com
@PrivacySandboxValue - java.lang.List: todos os elementos da lista precisam ser de um dos tipos de dados compatíveis.
Há algumas outras advertências:
- As classes de dados anotadas com
@PrivacySandboxValuenão podem conter campos do 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
@PrivacySandboxInterfaceou@PrivacySandboxCallback
APIs assíncronas
Como as APIs do SDK sempre fazem uma chamada para um processo separado, precisamos garantir que essas chamadas não bloqueiem a linha de execução de chamada do cliente.
Para isso, todos os métodos nas interfaces anotadas com
@PrivacySandboxService, @PrivacySandboxInterface e @PrivacySandboxCallback
precisam ser declarados explicitamente como APIs assíncronas.
As APIs assíncronas podem ser implementadas em Kotlin de duas maneiras:
- Use funções de suspensão.
- Aceite callbacks que recebem notificações quando a operação é concluída ou de outros eventos durante o progresso da operação. O tipo de retorno da função precisa ser uma unidade.
Exceções
As APIs do SDK não são compatíveis com nenhuma forma de exceções verificadas.
O código de shim gerado captura todas as exceções de tempo de execução geradas pelo SDK e
as gera como um PrivacySandboxException para o cliente com informações sobre
a causa envolvida.
Biblioteca de interface
Se você tiver interfaces que representam anúncios, como um banner, também precisará implementar a interface SandboxedUiAdapter para ativar a abertura de sessões para o anúncio carregado.
Essas sessões formam um canal lateral entre o cliente e o SDK e atendem a duas finalidades principais:
- Receber notificações sempre que uma mudança na interface acontecer.
- Notifique o cliente sobre mudanças na apresentação da interface.
Como o cliente pode usar a interface anotada com @PrivacySandboxService para
se comunicar com o SDK, qualquer API para carregar anúncios pode ser adicionada a essa
interface.
Quando o cliente solicitar o carregamento de um anúncio, carregue o anúncio e retorne uma instância da interface que implementa SandboxedUiAdapter. Isso permite que o cliente solicite a abertura de sessões para esse anúncio.
Quando o cliente solicita a abertura de uma sessão, o SDK ativado no tempo 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 uma mudança na interface acontece. Você pode usar 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 Runtime.
Suporte de atividade
Para iniciar atividades pertencentes ao SDK no Sandbox de privacidade, é necessário modificar a API do SDK para receber um objeto SdkActivityLauncher, também fornecido pela biblioteca de UI.
Por exemplo, a API do SDK a seguir precisa iniciar atividades e, portanto, 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 nela.
Um SDK ativado pelo ambiente de execução precisa implementar essa classe abstrata para gerar um ponto de entrada para que o SDK Runtime possa se comunicar com ele.
Para oferecer suporte à compatibilidade com versões anteriores, apresentamos as seguintes classes:
SandboxedSdkProviderAdapter, que estendeSandboxedSdkProvidere processa solicitações de carregamento do SDK, independente da disponibilidade do SDK Runtime. Usado internamente, declarado no módulo ASB.SandboxedSdkProviderCompat, uma classe abstrata que imita a interface doSandboxedSdkProvider.
Saiba mais sobre a compatibilidade com versões anteriores do SDK Runtime.
As ferramentas de geração de shim 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 da interface anotada.
// 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 um
Context e espera que sua interface anotada de ponto de entrada seja retornada.
Esse método recebe o nome da sua interface @PrivacySandboxService, adicionando
create ao nome. Por exemplo, se a interface se chamar MySdk, as ferramentas
gerarão createMySdk.
Para conectar totalmente seu ponto de entrada, forneça uma implementação da interface anotada @PrivacySandboxService no SDK ativado para execução ao AbstractSandboxedSdkProvider gerado.
class MySdkSandboxedSdkProvider : AbstractSandboxedSdkProvider() {
override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
}
Mudanças no módulo ASB
Você precisa declarar o nome de classe totalmente qualificado da sua implementação do SandboxedSdkProviderCompat no campo compatSdkProviderClassName do build.gradle do módulo ASB.
Essa é a classe que você implementou na etapa anterior. Modifique 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 seu ambiente de desenvolvimento Etapa 4: consumir o SDK ativado no tempo de execução