Xây dựng và sử dụng SDK hỗ trợ thời gian chạy

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

Tạo SDK hỗ trợ thời gian chạy

Bạn phải hoàn tất các bước sau đây để tạo SDK hỗ trợ thời gian chạy:

  1. Thiết lập cấu trúc dự án
  2. Chuẩn bị các phần phụ thuộc của dự án và mô-đun
  3. Thêm logic kinh doanh SDK
  4. Xác định API SDK
  5. Chỉ định một điểm truy cập cho SDK của bạn

Thiết lập cấu trúc dự án

Bạn nên sắp xếp dự án thành các mô-đun sau:

  1. Mô-đun ứng dụng – Ứng dụng kiểm thử mà bạn đang dùng để kiểm thử và phát triển ứng dụng SDK, thể hiện những gì ứng dụng thực của bạn sẽ có. Ứng dụng của bạn sẽ có phần phụ thuộc trên mô-đun thư viện quảng cáo hiện có (SDK nhận biết thời gian chạy).
  2. Mô-đun thư viện quảng cáo hiện tại (SDK nhận biết thời gian chạy) - Mô-đun thư viện Android có chứa mã "chưa được kích hoạt bởi thời gian chạy" hiện có Logic SDK, một cách tĩnh SDK được liên kết.
    • Để bắt đầu, bạn có thể chia nhỏ các chức năng. Ví dụ: một số mã có thể do SDK hiện tại xử lý và một số có thể được chuyển đến trình kích hoạt thời gian chạy đã kích hoạt SDK.
  3. Mô-đun thư viện quảng cáo hỗ trợ thời gian chạy – Chứa SDK hỗ trợ thời gian chạy của bạn logic kinh doanh. Bạn có thể tạo tệp này trên Android Studio dưới dạng thư viện Android .
  4. Mô-đun ASB hỗ trợ thời gian chạy – Xác định dữ liệu gói để gói mã SDK hỗ trợ thời gian chạy vào ASB.
    • Bạn phải tạo hồ sơ theo cách thủ công bằng com.android.privacy-sandbox-sdk. Bạn có thể thực hiện việc này bằng cách tạo một thư mục mới.
    • Mô-đun này không được chứa mã nào mà chỉ chứa một build.gradle trống có các phần phụ thuộc vào mô-đun thư viện quảng cáo hỗ trợ thời gian chạy. Nội dung của tệp này được xác định trong Chuẩn bị SDK.
    • Hãy nhớ đưa mô-đun này vào tệp settings.gradle và trong mô-đun thư viện quảng cáo hiện có.

Cấu trúc dự án trong hướng dẫn này là một đề xuất. Bạn có thể chọn một cho SDK của bạn và áp dụng các nguyên tắc kỹ thuật tương tự. Bạn luôn có thể tạo các mô-đun bổ sung khác để mô-đun hoá mã trong ứng dụng và các mô-đun thư viện.

Chuẩn bị SDK của bạn

Để chuẩn bị dự án cho việc phát triển SDK kích hoạt bởi thời gian chạy, bạn cần trước tiên, hãy xác định một số phần phụ thuộc của thư viện và công cụ:

  • Các thư viện có khả năng tương thích ngược trong Thời gian chạy SDK, hỗ trợ cho thiết bị không có Hộp cát về quyền riêng tư (Android 13 trở xuống) (androidx.privacysandbox.sdkruntime:)
  • Thư viện giao diện người dùng hỗ trợ trình bày quảng cáo (androidx.privacysandbox.ui:)
  • Công cụ cho nhà phát triển SDK để hỗ trợ việc khai báo API SDK và công cụ tạo shim (androidx.privacysandbox.tools:)
  1. Thêm cờ này vào tệp gradle.properties của dự án để bật khả năng tạo các SDK hỗ trợ thời gian chạy.

    # This enables the Privacy Sandbox for your project on Android Studio.
    android.experimental.privacysandboxsdk.enable=true
    android.experimental.privacysandboxsdk.requireServices=false
    
  2. Sửa đổi build.gradle của dự án để bao gồm các thư viện Jetpack của trình trợ giúp và các phần phụ thuộc khác:

    // 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. Cập nhật tệp build.gradle trong mô-đun thư viện quảng cáo hỗ trợ thời gian chạy (SDK RE) để đưa các phần phụ thuộc này vào.

    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. Thay thế tệp build.gradle trong mô-đun ASB hỗ trợ thời gian chạy bằng đoạn mã sau:

    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. Cập nhật tệp build.gradle trong mô-đun thư viện quảng cáo hiện có (SDK RA) để bao gồm các phần phụ thuộc sau:

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

Thêm logic kinh doanh SDK

Triển khai logic nghiệp vụ của SDK như cách bạn thường làm trong mô-đun thư viện quảng cáo kích hoạt bởi thời gian chạy.

Nếu bạn đang có SDK hiện có, hãy di chuyển nhiều chức năng logic kinh doanh, giao diện và dành cho hệ thống tuỳ thích ở giai đoạn này, nhưng tính đến việc di chuyển toàn bộ trong tương lai.

Nếu bạn cần quyền truy cập vào bộ nhớ, mã nhận dạng cho quảng cáo trên Google Play hoặc mã nhóm ứng dụng, hãy đọc các mục sau:

Sử dụng API lưu trữ trong SDK của bạn

Các SDK trong Thời gian chạy SDK không thể truy cập, đọc hoặc ghi trong bộ nhớ trong của ứng dụng nữa và ngược lại.

Thời gian chạy SDK được phân bổ vào vùng bộ nhớ trong riêng, tách biệt với ứng dụng.

Các SDK có thể truy cập vào bộ nhớ trong riêng biệt này thông qua các API lưu trữ tệp trên đối tượng Context do SandboxedSdkProvider#getContext() trả về.

SDK chỉ có thể dùng bộ nhớ trong nên chỉ các API bộ nhớ trong, chẳng hạn như Context.getFilesDir() hoặc Context.getCacheDir() công việc. Xem thêm ví dụ trong Truy cập từ bộ nhớ trong.

Không hỗ trợ quyền truy cập vào bộ nhớ ngoài trong Thời gian chạy SDK. Việc gọi các API để truy cập vào bộ nhớ ngoài sẽ gửi một ngoại lệ hoặc trả về giá trị rỗng. Danh sách sau đây bao gồm một số ví dụ:

Bạn phải sử dụng Context do SandboxedSdkProvider.getContext() trả về để lưu trữ. Việc sử dụng API lưu trữ tệp trên mọi thực thể đối tượng Context khác, chẳng hạn như ngữ cảnh của ứng dụng, không được đảm bảo sẽ hoạt động như mong đợi trong mọi trường hợp.

Đoạn mã sau đây minh hoạ cách sử dụng bộ nhớ trong Thời gian chạy 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"
    }
}

Ở bộ nhớ trong riêng biệt của từng Thời gian chạy SDK, mỗi SDK có một thư mục bộ nhớ riêng. Bộ nhớ trên mỗi SDK là sự phân tách theo logic bộ nhớ trong của Thời gian chạy SDK, giúp tính toán dung lượng bộ nhớ mà mỗi SDK sử dụng.

Tất cả API bộ nhớ trong trên đối tượng Context đều trả về một đường dẫn lưu trữ cho mỗi SDK.

Truy cập vào mã nhận dạng cho quảng cáo do Dịch vụ Google Play cung cấp

Nếu SDK của bạn cần quyền truy cập vào mã nhận dạng cho quảng cáo do Dịch vụ Google Play cung cấp, hãy sử dụng AdIdManager#getAdId() để truy xuất giá trị theo cách không đồng bộ.

Truy cập vào mã nhóm ứng dụng do Dịch vụ Google Play cung cấp

Nếu SDK của bạn cần quyền truy cập vào mã nhóm ứng dụng do Dịch vụ Google Play cung cấp, hãy sử dụng AppSetIdManager#getAppSetId() để truy xuất giá trị không đồng bộ.

Khai báo API SDK

Để có thể truy cập SDK hỗ trợ thời gian chạy bên ngoài thời gian chạy, bạn phải để xác định API mà ứng dụng (SDK RA hoặc ứng dụng khách) có thể sử dụng.

Sử dụng chú giải để khai báo các giao diện này.

Chú thích

Bạn cần khai báo API SDK trong Kotlin dưới dạng giao diện và lớp dữ liệu bằng cách sử dụng các chú thích sau:

Chú thích
@PrivacySandboxService
  • Xác định entry-point (điểm truy cập) cho SDK RE của bạn
  • Phải là duy nhất
@PrivacySandboxInterface
  • Cho phép mô-đun hoá hơn nữa và hiển thị giao diện
  • Có thể có nhiều thực thể
@PrivacySandboxValue
  • Cho phép gửi dữ liệu trên các quy trình
  • Tương tự như các cấu trúc bất biến, có thể trả về nhiều giá trị thuộc nhiều loại
@PrivacySandboxCallback
  • Khai báo các API bằng lệnh gọi lại
  • Cung cấp một kênh quay lại để gọi mã ứng dụng khách

Bạn cần xác định các giao diện và lớp này ở bất cứ đâu bên trong mô-đun thư viện quảng cáo kích hoạt bởi thời gian chạy.

Xem cách sử dụng các chú thích này trong các phần sau.

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

Các kiểu được hỗ trợ

API SDK hỗ trợ thời gian chạy hỗ trợ các loại sau đây:

  • Tất cả loại nguyên hàm trong ngôn ngữ lập trình Java (chẳng hạn như int, long, char, boolean, v.v.)
  • Chuỗi
  • Giao diện Kotlin được chú thích bằng @PrivacySandboxInterface hoặc @PrivacySandboxCallback
  • Các lớp dữ liệu Kotlin được chú thích bằng @PrivacySandboxValue
  • java.lang.List – tất cả các phần tử trong Danh sách phải là một trong những dữ liệu được hỗ trợ loại

Có một số cảnh báo khác:

  • Các lớp dữ liệu được chú thích bằng @PrivacySandboxValue không được chứa các trường loại @PrivacySandboxCallback
  • Loại dữ liệu trả về không được chứa các loại được chú thích bằng @PrivacySandboxCallback
  • Danh sách không được chứa các phần tử của loại được chú thích bằng @PrivacySandboxInterface hoặc @PrivacySandboxCallback

API không đồng bộ

Vì API SDK luôn thực hiện lệnh gọi đến một quy trình riêng biệt, nên chúng ta cần đảm bảo rằng các lệnh gọi này không chặn luồng gọi của ứng dụng.

Để đạt được điều này, tất cả phương thức trong giao diện được chú giải bằng @PrivacySandboxService, @PrivacySandboxInterface@PrivacySandboxCallback phải được khai báo rõ ràng là API không đồng bộ.

Bạn có thể triển khai API không đồng bộ trong Kotlin theo 2 cách:

  1. Dùng hàm tạm ngưng.
  2. Chấp nhận các lệnh gọi lại để nhận thông báo khi thao tác hoàn tất hoặc các sự kiện khác trong suốt tiến trình của thao tác. Loại dữ liệu trả về của hàm phải là một Đơn vị.

Ngoại lệ

API SDK không hỗ trợ bất kỳ hình thức ngoại lệ đã kiểm tra nào.

Mã shim được tạo sẽ bắt kịp mọi ngoại lệ đối với thời gian chạy do SDK gửi và gửi chúng dưới dạng PrivacySandboxException đến khách hàng kèm theo thông tin về ẩn nấp bên trong nó.

Thư viện giao diện người dùng

Nếu có giao diện đại diện cho Quảng cáo, chẳng hạn như biểu ngữ, thì bạn cũng cần triển khai giao diện SandboxedUiAdapter để bật các phiên mở cho quảng cáo đã tải.

Các phiên này tạo thành một kênh phụ giữa ứng dụng và SDK, đồng thời chúng thực hiện hai mục đích chính:

  • Nhận thông báo bất cứ khi nào có thay đổi về giao diện người dùng.
  • Thông báo cho khách hàng về mọi thay đổi trong bản trình bày giao diện người dùng.

Vì ứng dụng có thể sử dụng giao diện được chú giải bằng @PrivacySandboxService để giao tiếp với SDK của bạn, bạn có thể thêm mọi API để tải quảng cáo vào .

Khi khách hàng yêu cầu tải một quảng cáo, hãy tải quảng cáo và trả về một phiên bản của giao diện triển khai SandboxedUiAdapter. Điều này cho phép khách hàng yêu cầu phiên mở cho quảng cáo đó.

Khi khách hàng yêu cầu mở một phiên, SDK kích hoạt bởi thời gian chạy của bạn có thể tạo một chế độ xem quảng cáo bằng cách sử dụng phản hồi quảng cáo và ngữ cảnh được cung cấp.

Để đạt được điều này, hãy tạo một lớp triển khai giao diện SandboxedUiAdapter.Session và khi SandboxedUiAdapter.openSession() được gọi, hãy đảm bảo rằng bạn gọi client.onSessionOpened(), truyền một thực thể của lớp Session dưới dạng tham số.

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

Lớp này cũng nhận được thông báo bất cứ khi nào giao diện người dùng thay đổi. Bạn có thể ví dụ như sử dụng lớp này để thay đổi kích thước quảng cáo hoặc biết thời điểm cấu hình thay đổi.

Tìm hiểu thêm về các API Bản trình bày giao diện người dùng trong Thời gian chạy.

Hỗ trợ hoạt động

Để bắt đầu các hoạt động do SDK sở hữu từ Hộp cát về quyền riêng tư, bạn cần sửa đổi API SDK để nhận được đối tượng SdkActivityLauncher, cũng do thư viện giao diện người dùng cung cấp.

Ví dụ: API SDK sau đây sẽ khởi chạy các hoạt động, vì vậy API SDK dự kiến sẽ có tham số SdkActivityLauncher:

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

Điểm truy cập SDK

Lớp trừu tượng SandboxedSdkProvider đóng gói API mà Thời gian chạy SDK sử dụng để tương tác với SDK đã tải vào đó.

SDK hỗ trợ thời gian chạy phải triển khai lớp trừu tượng này để tạo một điểm truy cập giúp thời gian chạy SDK có thể giao tiếp với SDK đó.

Để hỗ trợ khả năng tương thích ngược, chúng tôi đã ra mắt các lớp sau:

Tìm hiểu thêm về khả năng tương thích ngược cho Thời gian chạy SDK.

Các công cụ tạo shim thêm một lớp trừu tượng khác: Chúng tạo một lớp trừu tượng có tên là AbstractSandboxedSdkProvider bằng giao diện mà bạn đã chú giải bằng @PrivacySandboxService.

Lớp này mở rộng SandboxedSdkProviderCompat và nằm trong cùng một gói với giao diện được chú giải của bạn.

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

Lớp được tạo này sẽ hiển thị một phương thức nhà máy trừu tượng duy nhất lấy một Context và dự kiến sẽ trả về giao diện được chú giải tại điểm nhập.

Phương thức này được đặt tên theo giao diện @PrivacySandboxService của bạn, thêm vào trước create vào tên. Ví dụ: nếu giao diện của bạn có tên là MySdk, thì công cụ tạo createMySdk.

Để kết nối đầy đủ điểm truy cập, bạn phải cung cấp phương thức triển khai giao diện có chú thích @PrivacySandboxService trong SDK kích hoạt bởi thời gian chạy với AbstractSandboxedSdkProvider đã tạo.

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

Các thay đổi đối với mô-đun ASB

Bạn cần khai báo tên lớp đủ điều kiện của phương thức triển khai SandboxedSdkProviderCompat trong trường compatSdkProviderClassName của tệp build.gradle của mô-đun ASB.

Đây là lớp mà bạn đã triển khai ở bước trước và bạn sẽ sửa đổi build.gradle trên Mô-đun ASB của mình như sau:

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

Bước 2: Thiết lập môi trường phát triển Bước 4: Sử dụng SDK hỗ trợ thời gian chạy