Key concepts | Set up your development environment | Build an RE SDK | Consume the RE SDK | Testing, and building for distribution |
פיתוח ערכת SDK שתואמת לזמן ריצה
כדי ליצור ערכת SDK שתואמת לזמן ריצה, צריך לפעול לפי השלבים הבאים:
- הגדרה של מבנה הפרויקט
- הכנת יחסי התלות של הפרויקטים והמודולים
- הוספת הלוגיקה העסקית של ה-SDK
- הגדרת ממשקי ה-API של ה-SDK
- ציון נקודת כניסה ל-SDK
הגדרת מבנה הפרויקט
מומלץ לארגן את הפרויקט במודולים הבאים:
- מודול אפליקציה – אפליקציית הבדיקה שבה אתם משתמשים כדי לבדוק ולפתח את SDK, שמייצג את מה שיהיו לקוחות האפליקציה האמיתיים שלכם. האפליקציה שלך צריכה תלויות במודול הקיים של ספריית המודעות (SDK מבוסס-זמן ריצה).
- מודול קיים של ספריית מודעות (SDK מבוסס-זמן ריצה) – מודול של ספרייה ב-Android
שמכיל את הערך הקיים 'שאינו מופעל זמן ריצה' לוגיקת SDK,
SDK מקושר.
- בתור התחלה, אפשר לפצל את היכולות. לדוגמה, חלק מהקודים יכולים להיות, מטופל ב-SDK הקיים, וחלקם יכולים להיות מנותבים להרצת זמן הריצה SDK.
- מודול של ספריית מודעות שפועל בסביבת זמן ריצה – מכיל את הלוגיקה העסקית של ערכת ה-SDK שפועלת בסביבת זמן ריצה. אפשר ליצור את היצירה הזו ב-Android Studio כספריית Android של מודל טרנספורמר.
- מודול ASB שמופעל עם זמן ריצה – מגדיר את נתוני החבילה כדי לקבץ את נתוני החבילה.
ל-ASB, בקוד SDK שמותאם לזמן ריצה.
- צריך ליצור אותו באופן ידני com.android.privacy-sandbox-sdk. כדי לעשות זאת, צריך ליצור ספרייה חדשה.
- המודול הזה לא יכול להכיל קוד אלא רק build.gradle ריק תלויים במודול של ספריית המודעות שהוגדר בזמן ריצה. התוכן של הקובץ הזה מוגדר כאן מכינים את ה-SDK.
- חשוב לכלול את המודול הזה בקובץ settings.gradle ובמודול הקיים של ספריית המודעות.
מבנה הפרויקט במדריך הזה הוא הצעה, אפשר לבחור של ה-SDK וליישם את אותם עקרונות טכניים. תמיד אפשר ליצור מודולים נוספים כדי לשנות את הקוד באפליקציה ובמודולים של הספרייה.
הכנת ה-SDK
כדי להכין את הפרויקט לפיתוח SDK שתואם לזמן ריצה, צריך: מגדירים תחילה כמה יחסי תלות של כלים וספריות:
- ספריות תאימות לאחור של זמן ריצה ל-SDK, שמספקות תמיכה
מכשירים שאין בהם ארגז חול לפרטיות (Android 13 ומטה)
(
androidx.privacysandbox.sdkruntime:
) - ספריות ממשק משתמש לתמיכה בהצגת מודעות (
androidx.privacysandbox.ui:
) - כלים למפתחים של ערכות SDK לתמיכה בהצהרה על SDK API וביצירת ספריית shim (
androidx.privacysandbox.tools:
)
כדי לאפשר את היכולת ליצור ערכות SDK תואמות זמן ריצה, מוסיפים את הדגל הזה לקובץ gradle.properties של הפרויקט.
# This enables the Privacy Sandbox for your project on Android Studio. android.experimental.privacysandboxsdk.enable=true android.experimental.privacysandboxsdk.requireServices=false
משנים את build.gradle של הפרויקט כך שיכלול את ספריות העזר של Jetpack וספריות תלויות אחרות:
// 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 }
מעדכנים את הקובץ build.gradle במודול של ספריית המודעות שתואמת לזמן ריצה (RE SDK) כך שיכלול את יחסי התלות האלה.
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" }
מחליפים את הקובץ build.gradle במודול ASB שתומך בזמן ריצה בערך הבא:
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>') }
מעדכנים את הקובץ build.gradle במודול של ספריית המודעות הקיימת (RA SDK) כך שיכלול את יחסי התלות הבאים:
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 או למזהה של קבוצת האפליקציות, כדאי לקרוא את הקטעים הבאים:
שימוש בממשקי API לאחסון ב-SDK
ערכות SDK בזמן הריצה של ה-SDK כבר לא יכולות לגשת, לקרוא או לכתוב באחסון הפנימי של האפליקציה ולהפך.
זמן הריצה של ה-SDK מוקצה לאזור אחסון פנימי משלו, בנפרד מהאפליקציה.
ערכות SDK יכולות לגשת לאחסון הפנימי הנפרד הזה באמצעות ממשקי ה-API לאחסון קבצים באובייקט Context
שמוחזר על ידי SandboxedSdkProvider#getContext()
.
ערכות SDK יכולות להשתמש רק באחסון פנימי, לכן רק ממשקי API של אחסון פנימי כמו Context.getFilesDir()
או
Context.getCacheDir()
פועלות. הצגת דוגמאות נוספות ב
גישה מאחסון פנימי.
אין תמיכה בגישה לאחסון חיצוני מזמן הריצה של ה-SDK. קריאה לממשקי API כדי לגשת לאחסון חיצוני תגרום להשלכת חריגה או להחזרת ערך null. הרשימה הבאה כוללת כמה דוגמאות:
- גישה לקבצים באמצעות Storage Access Framework גורמת לחריג אבטחה.
- הפונקציה
getExternalFilsDir()
תמיד מחזירה null.
עליך להשתמש בContext
שהוחזר על ידי SandboxedSdkProvider.getContext()
לאחסון. לא בטוח שהשימוש ב-File Storage API בכל מופע אחר של אובייקט Context
, כמו בהקשר של האפליקציה, יפעל כצפוי בכל מצב.
קטע הקוד הבא מדגים איך להשתמש באחסון בזמן הריצה של ה-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 משתמשת.
כל ממשקי ה-API של האחסון הפנימי באובייקט Context
מחזירים נתיב אחסון לכל ערכת SDK.
גישה למזהה הפרסום שסופק על ידי Google Play Services
אם ל-SDK שלכם דרושה גישה למזהה הפרסום שסופק על ידי Google Play Services, צריך להשתמש ב-AdIdManager#getAdId()
כדי לאחזר את הערך באופן אסינכררוני.
גישה למזהה של קבוצת האפליקציות שסופק על ידי Google Play Services
אם ל-SDK נדרשת גישה למזהה קבוצת האפליקציות שסופק על ידי Google Play Services, צריך להשתמש ב-
AppSetIdManager#getAppSetId()
כדי לאחזר את הערך באופן אסינכרוני.
הצהרה על ממשקי API של SDK
כדי שאפשר יהיה לגשת ל-SDK שתומך בסביבת זמן ריצה מחוץ לסביבת זמן הריצה, צריך להגדיר ממשקי API שלקוחות (RA SDK או אפליקציית הלקוח) יכולים להשתמש בהם.
משתמשים בהערות כדי להצהיר על הממשקים האלה.
הערות
צריך להצהיר על ממשקי SDK ב-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() }
סוגים נתמכים
ממשקי API ל-SDK המותאמים לזמן ריצה תומכים בסוגים הבאים:
- כל הסוגים הראשוניים בשפת Java (כגון int, ארוך, char, boolean וכו')
- מחרוזת
- ממשקי Kotlin עם הערות
@PrivacySandboxInterface
או@PrivacySandboxCallback
- מחלקות נתונים ב-Kotlin עם הערות
@PrivacySandboxValue
- java.lang.List – כל הפריטים ברשימה חייבים להיות מאחד מסוגי הנתונים הנתמכים
יש כמה אזהרות נוספות:
- כיתות נתונים עם הערה
@PrivacySandboxValue
לא יכולות להכיל שדות מסוג@PrivacySandboxCallback
- סוגי החזרה לא יכולים להכיל סוגים עם הערות עם
@PrivacySandboxCallback
- הרשימה לא יכולה להכיל אלמנטים מסוגים עם הערות
@PrivacySandboxInterface
או@PrivacySandboxCallback
ממשקי API אסינכררוניים
מכיוון שממשקי ה-API של ה-SDK תמיד מפעילים קריאה לתהליך נפרד, אנחנו צריכים לוודא שהשיחות האלה לא חוסמות את שרשור השיחות של הלקוח.
כדי להשיג זאת, כל השיטות בממשקים שנוספו עם הערות
@PrivacySandboxService
, @PrivacySandboxInterface
וגם @PrivacySandboxCallback
צריך להצהיר במפורש כממשקי API אסינכרוניים.
אפשר להטמיע ממשקי API אסינכרוניים ב-Kotlin בשתי דרכים:
- להשתמש בפונקציות השעיה.
- לקבל שיחות חוזרות (callback) שמקבלות הודעה כשהפעולה תסתיים, או אירועים אחרים במהלך התקדמות הפעולה. סוג ההחזרה של הפונקציה חייבת להיות יחידה.
חריגים
ממשקי ה-API של ה-SDK לא תומכים באף סוג של חריגות מאומתות.
קוד ה-shim שנוצר מתעד חריגים בסביבת זמן הריצה של ה-SDK,
זורקת אותן בתור PrivacySandboxException
ללקוח עם מידע על
הסיבה שעטופה.
ספרייה בממשק המשתמש
אם יש לכם ממשקים שמייצגים מודעות, כמו באנר, עליכם גם להטמיע את הממשק SandboxedUiAdapter
כדי לאפשר פתיחת סשנים של המודעה שנטענה.
הסשנים האלה יוצרים ערוץ צדדי בין הלקוח ל-SDK, למלא שתי מטרות עיקריות:
- קבלת התראות בכל פעם שמתרחש שינוי בממשק המשתמש.
- חשוב להודיע ללקוח על שינויים במצגת בממשק המשתמש.
מכיוון שהלקוח יכול להשתמש בממשק עם הערות @PrivacySandboxService
כדי
לתקשר עם ה-SDK שלך, ניתן להוסיף לו כל ממשקי API לטעינת מודעות
גרפי.
כאשר הלקוח מבקש לטעון מודעה, טוענים את המודעה ומחזירים מופע של
הממשק שמטמיע את SandboxedUiAdapter
. כך הלקוח יכול לבקש סשנים של פתיחה עבור המודעה הזו.
כשהלקוח מבקש לפתוח סשן, ערכת ה-SDK שתואמת לזמן ריצה יכולה ליצור צפייה במודעה על סמך התגובה לבקשה להצגת מודעה וההקשר שסופק.
כדי לעשות זאת, צריך ליצור מחלקה שמממשת את הממשק SandboxedUiAdapter.Session
, וכשנשלחת קריאה אל SandboxedUiAdapter.openSession()
, הקפידו לבצע קריאה אל client.onSessionOpened()
ולהעביר מופע של המחלקה Session
כפרמטר.
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)
}
}
}
הכיתה הזו מקבלת גם התראות בכל פעם שמתבצע שינוי בממשק המשתמש. אפשר אפשר להשתמש בסיווג הזה כדי לשנות את גודל המודעה, או לדעת מתי ההגדרות האישיות השתנו, למשל.
מידע נוסף על ממשקי API להצגת ממשק משתמש בסביבת זמן הריצה
תמיכה בפעילות
כדי להתחיל פעילויות בבעלות SDK מארגז החול לפרטיות, צריך לשנות את ה-SDK API כדי לקבל אובייקט SdkActivityLauncher
, שגם הוא מסופק על ידי ספריית ממשק המשתמש.
לדוגמה, ממשק ה-API הבא של SDK אמור להפעיל פעילויות, ולכן הוא מצפה לפרמטר SdkActivityLauncher
:
@PrivacySandboxInterface
interface FullscreenAd {
suspend fun show(activityLauncher: SdkActivityLauncher)
}
נקודת הכניסה ל-SDK
הכיתה התיאורטית SandboxedSdkProvider
מכילה את ה-API שבו זמן הריצה ל-SDK משתמש כדי ליצור אינטראקציה עם ערכות ה-SDK שנטענו בו.
צריך להטמיע את הכיתה הזוחית ב-SDK שתומך בסביבת זמן ריצה כדי ליצור נקודת כניסה שסביבת זמן הריצה של ה-SDK תוכל לתקשר איתה.
לצורך תמיכה בתאימות לאחור, השקנו את הסוגים הבאים:
SandboxedSdkProviderAdapter
, שמרחיב אתSandboxedSdkProvider
ומטפל בבקשות לטעינת SDK ללא קשר לזמינות של זמן הריצה של ה-SDK. הוא משמש באופן פנימי, מוצהר במודול ASB.SandboxedSdkProviderCompat
, מחלקה מופשטת שמחקה את הממשק שלSandboxedSdkProvider
.
מידע נוסף על תאימות לאחור של זמן הריצה של ה-SDK
הכלים ליצירת ספריית shim מוסיפים שכבה נוספת של הפשטה: הם יוצרים מחלקה מופשטת בשם AbstractSandboxedSdkProvider
באמצעות הממשק שהוספתם לו הערות ב-@PrivacySandboxService
.
הכיתה הזו מתרחבת ל-SandboxedSdkProviderCompat
ונמצאת באותה חבילה כמו הממשק עם ההערות.
// Auto-generated code.
abstract class AbstractSandboxedSdkProvider : SandboxedSdkProviderCompat {
abstract fun createMySdk(context: Context): MySdk
}
הכיתה שנוצרה חושפת שיטה אבסטרקטית אחת של מפעל, שמקבלת Context
ומצפה להחזרת הממשק המתויג של נקודת הכניסה.
השם של השיטה הזו נגזר משם ממשק @PrivacySandboxService
, עם התחילית create
. לדוגמה, אם השם של הממשק הוא MySdk
, הכלים יוצרים את createMySdk
.
כדי לחבר באופן מלא את נקודת הכניסה, עליך לספק הטמעה של @PrivacySandboxService
הממשק עם הערות ב-SDK שתואם לזמן ריצה ל-AbstractSandboxedSdkProvider
שנוצר.
class MySdkSandboxedSdkProvider : AbstractSandboxedSdkProvider() {
override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
}
שינויים במודול ASB
צריך להצהיר על שם המחלקה המלא של ההטמעה של SandboxedSdkProviderCompat
בשדה compatSdkProviderClassName
בקובץ build.gradle של מודול ה-ASB.
זו המחלקה שהטמעתם בשלב הקודם, וצריך לשנות את build.gradle במודול ASB באופן הבא:
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 שתואם לזמן הריצה