| Key concepts | Set up your development environment | Build an RE SDK | Consume the RE SDK | Testing, and building for distribution |
런타임 지원 SDK 빌드
런타임 지원 SDK를 빌드하려면 다음 단계를 완료해야 합니다.
프로젝트 구조 설정
프로젝트를 다음 모듈로 구성하는 것이 좋습니다.
- 앱 모듈 - SDK를 테스트하고 개발하는 데 사용하는 테스트 앱으로, 실제 앱 클라이언트가 갖게 되는 것을 나타냅니다. 앱에 기존 광고 라이브러리 모듈 (런타임 인식 SDK)에 대한 종속 항목이 있어야 합니다.
- 기존 광고 라이브러리 모듈(런타임 인식 SDK) - 기존의 '런타임 지원되지 않는' SDK 로직이 포함된 Android 라이브러리 모듈(정적으로 연결된 SDK)입니다.
- 처음에는 기능을 분할할 수 있습니다. 예를 들어 일부 코드는 기존 SDK에서 처리하고 일부는 런타임 지원 SDK로 라우팅할 수 있습니다.
- 런타임 지원 광고 라이브러리 모듈 - 런타임 지원 SDK 비즈니스 로직을 포함합니다. Android 스튜디오에서 Android 라이브러리 모듈로 만들 수 있습니다.
- 런타임 지원 ASB 모듈 - 런타임 지원 SDK 코드를 ASB로 번들로 묶을 패키지 데이터를 정의합니다.
- com.android.privacy-sandbox-sdk 유형을 사용하여 수동으로 만들어야 합니다. 새 디렉터리를 만들어 이 작업을 수행할 수 있습니다.
- 이 모듈에는 코드가 포함되어서는 안 되며 런타임 지원 광고 라이브러리 모듈에 대한 종속 항목이 있는 빈 build.gradle 파일만 포함되어야 합니다. 이 파일의 콘텐츠는 SDK 준비에 정의되어 있습니다.
- 이 모듈을 settings.gradle 파일과 기존 광고 라이브러리 모듈에 포함해야 합니다.
이 가이드의 프로젝트 구조는 제안사항이며, SDK에 다른 구조를 선택하고 동일한 기술 원칙을 적용할 수 있습니다. 언제든지 다른 추가 모듈을 만들어 앱과 라이브러리 모듈의 코드를 모듈화할 수 있습니다.
SDK 준비
런타임 지원 SDK 개발을 위해 프로젝트를 준비하려면 먼저 일부 도구 및 라이브러리 종속 항목을 정의해야 합니다.
- 개인 정보 보호 샌드박스가 없는 기기 (Android 13 이하)를 지원하는 SDK 런타임 하위 호환성 라이브러리(
androidx.privacysandbox.sdkruntime:) - 광고 표시를 지원하는 UI 라이브러리 (
androidx.privacysandbox.ui:) - SDK API 선언 및 shim 생성을 지원하는 SDK 개발자 도구(
androidx.privacysandbox.tools:)
이 플래그를 프로젝트의 gradle.properties 파일에 추가하여 런타임 지원 SDK를 만드는 기능을 사용 설정합니다.
# This enables the Privacy Sandbox for your project on Android Studio. android.experimental.privacysandboxsdk.enable=true android.experimental.privacysandboxsdk.requireServices=false도우미 Jetpack 라이브러리 및 기타 종속 항목을 포함하도록 프로젝트의 build.gradle을 수정합니다.
// 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 }이러한 종속 항목을 포함하도록 런타임 지원 광고 라이브러리 (RE SDK) 모듈의 build.gradle 파일을 업데이트합니다.
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" }런타임 지원 ASB 모듈의 build.gradle 파일을 다음으로 바꿉니다.
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>') }기존 광고 라이브러리 (RA SDK) 모듈의 build.gradle 파일을 업데이트하여 다음 종속 항목을 포함합니다.
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" }
SDK 비즈니스 로직 추가
런타임 지원 광고 라이브러리 모듈 내에서 정기적으로 SDK의 비즈니스 로직을 구현합니다.
이전할 기존 SDK가 있는 경우 이 단계에서 원하는 만큼 비즈니스 로직, 인터페이스, 시스템 지향 함수를 이동하되 향후 전체 이전을 고려하세요.
저장소, Google Play 광고 ID 또는 앱 세트 ID에 액세스해야 하는 경우 다음 섹션을 참고하세요.
SDK에서 스토리지 API 사용
SDK 런타임의 SDK는 더 이상 앱의 내부 저장소에 액세스하거나 이를 읽거나 쓸 수 없으며, 그 반대도 불가능합니다.
SDK 런타임은 앱과 분리된 자체 내부 저장소 영역에 할당됩니다.
SDK는 SandboxedSdkProvider#getContext()에서 반환하는 Context 객체의 File Storage API를 사용하여 별도의 이 내부 저장소에 액세스할 수 있습니다.
SDK는 내부 저장소만 사용할 수 있으므로 Context.getFilesDir() 또는 Context.getCacheDir() 같은 내부 저장소 API만 작동합니다. 더 많은 예는 내부 저장소에서 액세스를 참고하세요.
SDK 런타임에서는 외부 저장소에 액세스할 수 없습니다. 외부 저장소에 액세스하기 위해 API를 호출하면 예외가 발생하거나 null이 반환됩니다. 다음은 몇 가지 예입니다.
- 저장소 액세스 프레임워크를 사용하여 파일에 액세스하면 SecurityException이 발생합니다.
getExternalFilsDir()은 항상 null을 반환합니다.
SandboxedSdkProvider.getContext()에서 반환된 Context를 사용하여 저장해야 합니다. 애플리케이션 컨텍스트와 같은 다른 Context 객체 인스턴스에 File Storage API를 사용하면 일부 상황에서는 예상대로 작동하지 않을 수도 있습니다.
다음 코드 스니펫은 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" } }
각 SDK 런타임의 별도 내부 저장소 내에서 각 SDK에는 자체 저장소 디렉터리가 있습니다. SDK별 저장소는 SDK 런타임의 내부 저장소를 논리적으로 분리한 것으로, 각 SDK에서 사용 중인 저장용량을 확인하는 데 도움이 됩니다.
Context 객체의 모든 내부 저장소 API가 각 SDK의 저장소 경로를 반환합니다.
Google Play 서비스에서 제공하는 광고 ID에 액세스
SDK가 Google Play 서비스에서 제공하는 광고 ID에 액세스해야 하는 경우 AdIdManager#getAdId()를 사용하여 값을 비동기적으로 가져옵니다.
Google Play 서비스에서 제공하는 앱 세트 ID에 액세스
SDK가 Google Play 서비스에서 제공하는 앱 세트 ID에 액세스해야 하는 경우 AppSetIdManager#getAppSetId()를 사용하여 값을 비동기적으로 가져옵니다.
SDK API 선언
런타임 지원 SDK가 런타임 외부에서 액세스할 수 있으려면 클라이언트 (RA SDK 또는 클라이언트 앱)가 사용할 수 있는 API를 정의해야 합니다.
주석을 사용하여 이러한 인터페이스를 선언합니다.
주석
SDK API는 다음 주석을 사용하여 Kotlin에서 인터페이스 및 데이터 클래스로 선언해야 합니다.
| 주석 | |
|---|---|
@PrivacySandboxService |
|
@PrivacySandboxInterface |
|
@PrivacySandboxValue |
|
@PrivacySandboxCallback |
|
런타임 지원 광고 라이브러리 모듈 내 어디에서나 이러한 인터페이스와 클래스를 정의해야 합니다.
이러한 주석의 사용법은 다음 섹션을 참고하세요.
@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() }
지원되는 유형
런타임 지원 SDK API는 다음 유형을 지원합니다.
- Java 프로그래밍 언어의 모든 기본 유형 (예: int, long, char, boolean 등)
- 문자열
@PrivacySandboxInterface또는@PrivacySandboxCallback로 주석이 추가된 Kotlin 인터페이스@PrivacySandboxValue로 주석이 지정된 Kotlin 데이터 클래스- java.lang.List - 목록의 모든 요소는 지원되는 데이터 유형 중 하나여야 합니다.
추가 주의사항이 있습니다.
@PrivacySandboxValue로 주석이 추가된 데이터 클래스에는@PrivacySandboxCallback유형의 필드가 포함될 수 없습니다.- 반환 유형은
@PrivacySandboxCallback로 주석이 달린 유형을 포함할 수 없습니다. - 목록에
@PrivacySandboxInterface또는@PrivacySandboxCallback로 주석이 추가된 유형의 요소가 포함될 수 없음
비동기 API
SDK API는 항상 별도의 프로세스를 호출하므로 이러한 호출이 클라이언트의 호출 스레드를 차단하지 않도록 해야 합니다.
이를 위해 @PrivacySandboxService, @PrivacySandboxInterface, @PrivacySandboxCallback로 주석이 지정된 인터페이스의 모든 메서드는 비동기 API로 명시적으로 선언되어야 합니다.
비동기 API는 Kotlin에서 다음 두 가지 방법으로 구현할 수 있습니다.
- 정지 함수를 사용합니다.
- 작업이 완료되거나 작업 진행 중에 다른 이벤트가 발생할 때 알림을 받는 콜백을 허용합니다. 함수의 반환 유형은 Unit이어야 합니다.
예외
SDK API는 확인된 예외를 지원하지 않습니다.
생성된 shim 코드는 SDK에서 발생한 런타임 예외를 포착하고 원인에 관한 정보가 내부에 래핑된 PrivacySandboxException로 클라이언트에 예외를 발생시킵니다.
UI 라이브러리
배너와 같은 광고를 나타내는 인터페이스가 있는 경우 로드된 광고의 세션을 열 수 있도록 SandboxedUiAdapter 인터페이스도 구현해야 합니다.
이러한 세션은 클라이언트와 SDK 간의 측면 채널을 형성하며 다음과 같은 두 가지 주요 목적을 충족합니다.
- UI 변경이 발생할 때마다 알림을 받습니다.
- UI 표시의 변경사항을 클라이언트에게 알립니다.
클라이언트가 @PrivacySandboxService로 주석이 추가된 인터페이스를 사용하여 SDK와 통신할 수 있으므로 광고 로드용 API를 이 인터페이스에 추가할 수 있습니다.
클라이언트가 광고 로드를 요청하면 광고를 로드하고 SandboxedUiAdapter를 구현하는 인터페이스의 인스턴스를 반환합니다. 이를 통해 클라이언트는 해당 광고의 세션 열기를 요청할 수 있습니다.
클라이언트가 세션을 열도록 요청하면 런타임 지원 SDK는 제공된 광고 응답과 컨텍스트를 사용하여 광고 뷰를 만들 수 있습니다.
이를 위해 SandboxedUiAdapter.Session 인터페이스를 구현하는 클래스를 만들고 SandboxedUiAdapter.openSession()가 호출될 때 Session 클래스의 인스턴스를 매개변수로 전달하여 client.onSessionOpened()를 호출해야 합니다.
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)
}
}
}
이 클래스는 UI 변경이 발생할 때마다 알림도 수신합니다. 이 클래스를 사용하여 광고 크기를 조정하거나 구성이 변경된 시점을 알 수 있습니다.
런타임의 UI 프레젠테이션 API에 대해 자세히 알아보기
활동 지원
개인 정보 보호 샌드박스에서 SDK 소유 활동을 시작하려면 UI 라이브러리에서 제공하는 SdkActivityLauncher 객체를 수신하도록 SDK API를 수정해야 합니다.
예를 들어 다음 SDK API는 활동을 실행해야 하므로 SdkActivityLauncher 매개변수가 필요합니다.
@PrivacySandboxInterface
interface FullscreenAd {
suspend fun show(activityLauncher: SdkActivityLauncher)
}
SDK 진입점
추상 클래스 SandboxedSdkProvider은 SDK 런타임이 로드된 SDK와 상호작용하는 데 사용하는 API를 캡슐화합니다.
런타임 지원 SDK는 SDK 런타임이 통신할 수 있는 진입점을 생성하기 위해 이 추상 클래스를 구현해야 합니다.
이전 버전과의 호환성을 지원하기 위해 다음 클래스가 도입되었습니다.
SandboxedSdkProvider을 확장하고 SDK 런타임 사용 가능 여부와 관계없이 SDK 로드 요청을 처리하는SandboxedSdkProviderAdapter내부적으로 사용되며 ASB 모듈에 선언됩니다.SandboxedSdkProvider의 인터페이스를 모방하는 추상 클래스인SandboxedSdkProviderCompat
심 생성 도구는 또 다른 추상화 레이어를 추가합니다. @PrivacySandboxService로 주석을 단 인터페이스를 사용하여 AbstractSandboxedSdkProvider라는 추상 클래스를 생성합니다.
이 클래스는 SandboxedSdkProviderCompat를 확장하며 주석이 지정된 인터페이스와 동일한 패키지에 있습니다.
// Auto-generated code.
abstract class AbstractSandboxedSdkProvider : SandboxedSdkProviderCompat {
abstract fun createMySdk(context: Context): MySdk
}
이 생성된 클래스는 Context를 사용하고 주석이 지정된 진입점 인터페이스가 반환될 것으로 예상하는 단일 추상 팩토리 메서드를 노출합니다.
이 메서드는 @PrivacySandboxService 인터페이스의 이름을 따서 지정되며 이름 앞에 create가 붙습니다. 예를 들어 인터페이스 이름이 MySdk이면 도구에서 createMySdk를 생성합니다.
진입점을 완전히 연결하려면 런타임 지원 SDK에서 @PrivacySandboxService 주석이 달린 인터페이스의 구현을 생성된 AbstractSandboxedSdkProvider에 제공해야 합니다.
class MySdkSandboxedSdkProvider : AbstractSandboxedSdkProvider() {
override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
}
ASB 모듈 변경사항
ASB 모듈의 build.gradle에 있는 compatSdkProviderClassName 필드에 SandboxedSdkProviderCompat 구현의 정규화된 클래스 이름을 선언해야 합니다.
이 클래스는 이전 단계에서 구현한 클래스이며 ASB 모듈의 build.gradle을 다음과 같이 수정합니다.
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"
}
2단계: 개발 환경 설정 4단계: 런타임 지원 SDK 사용