תאימות לאחור ל-SDK Runtime (זמן ריצה ל-SDK)

במסמך הזה מוצעת ספריית Jetpack חדשה שתעזור למפתחים לבצע מיגרציה אל זמן הריצה ל-SDK. המאמר מסביר איך תהיה תמיכה בזמן הריצה ל-SDK בגרסאות קודמות של פלטפורמת Android (מהבנייה ועד ההפעלה), ומהם ההבדלים או המגבלות בסביבת זמן הריצה שמפתחים יכולים לצפות להם. הספרייה הזו מאפשרת למפתחים ליצור גרסה אחת של האפליקציה או של ה-SDK שלהם, שכוללת את היכולת לפעול במכשירים עם תמיכה בזמן ריצה ל-SDK או בלי תמיכה כזו.

התאימות לאחור מושגת באמצעות הרכיבים הבאים:

  • Android Gradle Plugin‏ (AGP) + Bundletool יוצרים וריאציה של אפליקציה למכשירים ללא תמיכה ב-SDK Runtime על ידי הוספת SDK Runtime ל-APK.

  • ספריית הלקוח של SDK Runtime (androidx.privacysandbox.sdkruntime:sdkruntime-client) טוענת את ה-SDK בחבילה מנכסי האפליקציה ומבצעת אמולציה של SDK Runtime במכשירים ללא תמיכה ב-SDK Runtime.

  • ספריית ספק SDK Runtime (androidx.privacysandbox.sdkruntime:sdkruntime-provider) מספקת API לערכות SDK כדי לאפשר טעינה מספריית הלקוח של SDK Runtime.

משלוח באמצעות SDK עם Bundletool

במכשירים עם תמיכה ב-SDK Runtime, ערכות ה-SDK יסופקו ויותקנו כחבילות נפרדות.

כדי לתמוך בגרסאות פלטפורמה שלא כוללות תמיכה ב-SDK Runtime,‏ Bundletool ייצור וריאנט אחד או יותר של קובץ ה-APK של האפליקציה, שיכלול את כל ערכות ה-SDK שהאפליקציה תלויה בהן. כל SDK ארוז כפיצול APK נפרד. בנוסף, מתבצעים השינויים הבאים:

  1. מעתיקים קבצים של בייטקוד (DEX) של SDK לפיצול ה-SDK כנכסים.
  2. מעתיקים משאבי SDK Java לפיצול ה-SDK כנכסים.
  3. מיפוי מחדש של משאבי ה-SDK ומיזוג שלהם עם משאבי האפליקציה.
  4. יצירת קובצי הגדרות לספריית הלקוח של SDK Runtime.

טעינת ערכות SDK באמצעות ספריית הלקוח של SDK Runtime

ספריית הלקוח של זמן הריצה ל-SDK מספקת ממשקי API שדומים לממשקי API של הפלטפורמה, אבל תומכים גם בערכות SDK בסביבת זמן הריצה ל-SDK וגם בערכות SDK שצורפו לאפליקציית הווריאציה.

כדי להשתמש בספריית הלקוח של זמן הריצה ל-SDK, מוסיפים את התלות androidx.privacysandbox.sdkruntime:sdkruntime-client ומשתמשים ב-SdkSandboxManagerCompat במקום ב-SdkSandboxManager.

כשאפליקציה מנסה לטעון SDK, הספרייה בודקת קודם אם ה-SDK צורף לאפליקציה במהלך הבנייה. אם היא נארזה, הספרייה מחלצת את ה-SDK מהפיצול של ה-SDK וטוענת אותו לתהליך של האפליקציה. אם ה-SDK לא צורף לאפליקציה, הספרייה מעבירה את ה-API של הפלטפורמה לטעינת ה-SDK.

חילוץ SDK מנכסים

כשמנסים לטעון ערכת SDK שצורפה לאפליקציה, ספריית הלקוח של זמן הריצה ל-SDK בודקת אם קובצי ה-DEX של ערכת ה-SDK כבר חולצו לאחסון המכשיר (code_cache). אם לא, היא מחלצת אותם מנכסים.

בדרך כלל, הספרייה תחלץ קבצים רק פעם אחת אחרי התקנה או עדכון של אפליקציה.

אם המקום הפנוי באחסון קטן מהסף המותר (נכון לעכשיו, ‎100 MB) ולא חולצו קובצי DEX, הספרייה מנסה לטעון את ה-SDK ישירות מנכסים במכשירים נתמכים (API 27 ומעלה). התוצאה היא טביעת רגל גדולה יותר בזיכרון.

Classloader לשיעורי SDK

כדי למנוע התנגשויות בין ערכות SDK לבין מחלקות של אפליקציות, כל המחלקות של ערכות ה-SDK נטענות באמצעות טוען מחלקות נפרד, שלא תלוי בכלל בטוען המחלקות של האפליקציה הראשית.

בארכיטקטורה הנוכחית של זמן הריצה ל-SDK, כל התקשורת בין אפליקציה לבין ערכות SDK מתבצעת באמצעות קריאות ל-Binder IPC. אותם אובייקטים של SDK Binder משמשים ל-SDKs בחבילה, וסדרת הנתונים של עסקאות Binder SDK מאפשרת למפתחי אפליקציות להטיל אובייקטים של SDK Binder על ממשקי SDK Binder בצד האפליקציה.

באינטראקציות פנימיות אחרות (כמו הפעלה של SDK, אספקת ממשק API של בקר ל-SDK וכו'), הספרייה משתמשת ב-Reflection וב-Dynamic Proxies כדי לפעול ב-classloaders שונים.

סביבת SDK

ספריית SDKRuntime Provider מספקת ממשקי API למפתחי SDK. ממשקי ה-API האלה דומים לממשקי API של פלטפורמות, אבל הם מאפשרים לטעון ערכות SDK גם על ידי סביבת זמן הריצה של SDK וגם על ידי ספריית הלקוח של SDK Runtime.

כדי להשתמש ב-SDK של הספרייה, צריך להוסיף את התלות androidx.privacysandbox.sdkruntime:sdkruntime-provider ולהרחיב את SandboxedSdkProviderCompat במקום את SandboxedSdkProvider.

בנוסף, צריך להשתמש ב-SandboxedSdkProviderAdapter כספק ה-SDK כדי לאפשר טעינה של ספק התאימות בסביבת זמן הריצה של ה-SDK.

SdkSandboxControllerCompat מעביר את הפעולה אל ה-API של הפלטפורמה כשערכת ה-SDK נטענת בזמן הריצה של ה-SDK, או מעביר את הפעולה אל ספריית הלקוח של זמן הריצה של ה-SDK כשערכת ה-SDK נטענת כ-SDK מאוגד.

במקרה של ערכות SDK בחבילה, הספרייה משנה את סביבת ה-SDK בדרכים שמדמות התנהגות דומה לסביבת זמן הריצה של ה-SDK.

בקטעים הבאים מתוארים אופני הפעולה הצפויים כשה-SDK נטען על ידי ספריית הלקוח SDKRuntime.

מקורות מידע על SDK

יש תמיכה במשאבי SDK ‏ (res/) כשה-SDK נטען בתהליך האפליקציה. ‫Bundletool ממזג את כל המשאבים של ה-SDK עם המשאבים של האפליקציה.

כדי למנוע קונפליקטים, משאבי ה-SDK ממופים מחדש על ידי שינוי התחילית packageId בכל מזהי המשאבים.

כשספריית הלקוח SDKRuntime טוענת את ה-SDK, ‏ packageId מתעדכן בזמן הריצה כדי לאפשר פנייה למשאבים שמופו מחדש באמצעות המחלקה R.

משאבים ל-Java

יש תמיכה במשאבי Java כשה-SDK נטען בתהליך של האפליקציה. ‫Bundletool מעתיק את כל משאבי ה-SDK של Java לספרייה מיוחדת בנכסי האפליקציה. ספריית הלקוח SDKRuntime משתמשת בטוען מחלקות ביניים כדי להפנות מחדש את כל הקריאות שקשורות למשאבי Java אל ספריית הבסיס החדשה.

נכסי SDK

נכסי ה-SDK מוזגו עם נכסי האפליקציה בלי מיפוי מחדש.

SDK Storage

כדי לתמוך באחסון SDK, ספריית הלקוח של זמן הריצה של ה-SDK יוצרת ספריית שורש ייעודית לכל SDK שצורף לאחסון האפליקציה, ומספקת הקשר מיוחד שמשתמש בספרייה הזו כשורש האחסון.

אפשר לאחזר את ההקשר הזה מ-SandboxedSdkProviderCompat#getContext.

שיטות שקשורות לאחסון שנתמכות:

  • getDataDir
  • getCacheDir
  • getCodeCacheDir
  • getNoBackupFilesDir
  • getDir
  • getFilesDir
  • openFileInput
  • openFileOutput
  • deleteFile
  • getFileStreamPath
  • fileList
  • getDatabasePath
  • openOrCreateDatabase
  • moveDatabaseFrom – רק בין הקשרים של SDK
  • deleteDatabase
  • databaseList
  • getSharedPreferences
  • moveSharedPreferencesFrom – רק בין הקשרים של SDK
  • deleteSharedPreferences

אפשר ליצור הקשר של אחסון מוגן במכשיר על ידי קריאה ל-createDeviceProtectedStorageContext() בהקשר הזה.

SdkSandboxControllerCompat

ספריית הלקוח SDKRuntime מספקת את SdkSandboxControllerCompat ההטמעה לערכות SDK שצורפו ונטענו בתהליך האפליקציה.

אם ממשקי ה-API לא נתמכים על ידי ספריית הלקוח (לדוגמה, אם נעשה שימוש ב-SDK שנבנה עם גרסה של הספרייה שעדכנית יותר מגרסת האפליקציה), ייעשה שימוש בחלופה המתאימה ביותר (no-op או חריגה).

ניהול גרסאות

כשספריית הלקוח SDKRuntime טוענת SDK בחבילה, היא מבצעת לחיצת יד עם ספריית הספק SDKRuntime בתוך ה-SDK. במהלך הלחיצה, הספריות מחליפות את הגרסאות שלהן ומתאימות את ההתנהגות כדי להחליף ממשקי API שלא זמינים בחלופה המתאימה ביותר (no-op או חריגה).

מומלץ מאוד למפתחי אפליקציות ולמפתחי SDK להשתמש בגרסה העדכנית ביותר של הספרייה, אחרת יכול להיות שלא תהיה להם גישה לפונקציות שדורשות תמיכה בשני החלקים.

כל גרסה של ספריית הלקוח SDKRuntime יכולה לטעון SDK עם כל גרסה של ספריית הספק SDKRuntime, ולהיפך.

בעתיד, הערך הזה ישתנה לגרסה המינימלית של ספריית הלקוח שנדרשת לטעינת ה-SDK עם גרסה מסוימת של ספריית הספק.

כך אפשר לצמצם את הפיצול ולוודא שרוב ממשקי ה-API ייתמכו אם ה-SDK המצורף נטען בהצלחה.