| 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, שמייצגת את מה שיהיה ללקוחות האפליקציה האמיתית שלכם. האפליקציה צריכה להיות תלויה במודול הקיים של ספריית המודעות (runtime-aware SDK).
- מודול קיים של ספריית מודעות (SDK עם מודעות בזמן ריצה) – מודול של ספריית Android
שכולל את הלוגיקה הקיימת של ה-SDK 'ללא הפעלה בזמן ריצה', SDK עם קישור סטטי.
- בתור התחלה, אפשר לפצל את היכולות. לדוגמה, חלק מהקוד יכול להיות מטופל על ידי ה-SDK הקיים, וחלק יכול להיות מנותב ל-SDK תואם זמן ריצה.
- מודול ספריית מודעות שמופעל בזמן ריצה – מכיל את הלוגיקה העסקית של ה-SDK שמופעל בזמן ריצה אפשר ליצור אותו ב-Android Studio כמודול של ספריית Android.
- מודול ASB שתואם לזמן ריצה – מגדיר את נתוני החבילה כדי לארוז את קוד ה-SDK שתואם לזמן ריצה ב-ASB.
- צריך ליצור אותו באופן ידני באמצעות הסוג com.android.privacy-sandbox-sdk. אפשר לעשות את זה על ידי יצירת ספרייה חדשה.
- המודול הזה לא אמור להכיל קוד, אלא רק קובץ build.gradle ריק עם תלויות במודול ספריית המודעות שמופעל בזמן ריצה. התוכן של הקובץ הזה מוגדר במאמר הכנת ה-SDK.
- חשוב לכלול את המודול הזה בקובץ settings.gradle ובמודול ספריית המודעות הקיים.
מבנה הפרויקט במדריך הזה הוא הצעה, ואתם יכולים לבחור מבנה אחר ל-SDK ולהחיל את אותם עקרונות טכניים. תמיד אפשר ליצור מודולים נוספים כדי להפוך את הקוד באפליקציה ובמודולים של הספרייה למודולרי.
הכנת ה-SDK
כדי להכין את הפרויקט לפיתוח של ערכות SDK תואמות זמן ריצה, צריך קודם להגדיר כמה יחסי תלות של כלים וספריות:
- ספריות של SDK Runtime עם תאימות לאחור, שמספקות תמיכה במכשירים שאין בהם ארגז חול לפרטיות (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 Runtime. קריאה לממשקי API כדי לגשת לאחסון חיצוני תגרום לחריגה או להחזרת ערך null. הרשימה הבאה כוללת כמה דוגמאות:
- גישה לקבצים באמצעות framework של גישה לאחסון גורמת ל-SecurityException.
- הפונקציה
getExternalFilsDir()תמיד מחזירה null.
צריך להשתמש בערך Context שמוחזר על ידי SandboxedSdkProvider.getContext() לאחסון. השימוש ב-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 או אפליקציית הלקוח) יכולים להשתמש בהם.
כדי להצהיר על הממשקים האלה, משתמשים בהערות.
הערות
צריך להצהיר על ממשקי ה-API של ה-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, long, char, boolean וכו')
- מחרוזת
- ממשקי Kotlin עם ההערות
@PrivacySandboxInterfaceאו@PrivacySandboxCallback - מחלקות נתונים ב-Kotlin עם ההערה
@PrivacySandboxValue - java.lang.List – כל האלמנטים ברשימה חייבים להיות אחד מסוגי הנתונים הנתמכים
יש עוד כמה נקודות שחשוב לשים לב אליהן:
- מחלקות נתונים עם הערות מסוג
@PrivacySandboxValueלא יכולות להכיל שדות מסוג@PrivacySandboxCallback - סוגי החזרה לא יכולים להכיל סוגים עם הערה
@PrivacySandboxCallback - הרשימה לא יכולה להכיל רכיבים מהסוגים שמסומנים ב-
@PrivacySandboxInterfaceאו ב-@PrivacySandboxCallback
ממשקי API אסינכרוניים
ממשקי ה-API של ה-SDK תמיד מבצעים קריאה לתהליך נפרד, ולכן אנחנו צריכים לוודא שהקריאות האלה לא חוסמות את השרשור של הלקוח.
כדי לעשות את זה, צריך להצהיר במפורש על כל השיטות בממשקי ה-API האסינכרוניים עם ההערות @PrivacySandboxService, @PrivacySandboxInterface ו-@PrivacySandboxCallback.
אפשר להטמיע ממשקי API אסינכרוניים ב-Kotlin בשתי דרכים:
- משתמשים בהשעיית פונקציות.
- לקבל קריאות חוזרות (callback) שמתקבלות כשהפעולה מסתיימת, או על אירועים אחרים במהלך התקדמות הפעולה. סוג ההחזרה של הפונקציה חייב להיות Unit.
חריגים
ממשקי ה-API של ה-SDK לא תומכים באף סוג של חריגים שנבדקו.
קוד ה-shim שנוצר מאתר חריגות בזמן ריצה שמופעלות על ידי ה-SDK ומפעיל אותן כ-PrivacySandboxException ללקוח עם מידע על הסיבה שמופיע בתוך ה-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, צריך להטמיע את המחלקה המופשטת הזו ב-SDK שתואם לזמן ריצה.
כדי לתמוך בתאימות לאחור, הוספנו את המחלקות הבאות:
-
SandboxedSdkProviderAdapter, שמרחיב אתSandboxedSdkProviderומטפל בבקשות לטעינת SDK בלי קשר לזמינות של זמן הריצה ל-SDK. הוא נמצא בשימוש פנימי, ומוצהר במודול ASB. -
SandboxedSdkProviderCompat, מחלקה מופשטת שמדמה את הממשק שלSandboxedSdkProvider.
מידע נוסף על תאימות לאחור של SDK Runtime
הכלים ליצירת shim מוסיפים עוד שכבת הפשטה: הם יוצרים מחלקה מופשטת בשם AbstractSandboxedSdkProvider באמצעות הממשק שציינתם באמצעות @PrivacySandboxService.
הכיתה הזו מרחיבה את SandboxedSdkProviderCompat והיא נמצאת באותו חבילה כמו הממשק עם ההערות.
// Auto-generated code.
abstract class AbstractSandboxedSdkProvider : SandboxedSdkProviderCompat {
abstract fun createMySdk(context: Context): MySdk
}
הכיתה שנוצרת חושפת שיטה אחת של מפעל מופשט שמקבלת Context ומצפה להחזרת הממשק עם ההערה של נקודת הכניסה.
השם של השיטה הוא שם הממשק @PrivacySandboxService עם הקידומת create. לדוגמה, אם הממשק נקרא MySdk, כלי ה-AI יוצרים 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 עם הפעלה בזמן ריצה