תמיכה בגודלי דפים של 16 KB

בעבר, מערכת Android תמכה רק בדפים עם זיכרון בגודל 4KB, מה שאפשר למערכת לייעל את הביצועים של הזיכרון הכולל במכשירים עם נפח זיכרון ממוצע. החל מ-Android 15, ‏ AOSP תומך במכשירים שמגדירים בהם גודל דף של 16KB (מכשירים עם 16KB). אם האפליקציה שלך משתמשת בספריות NDK, באופן ישיר או בעקיפין דרך SDK, תצטרכו לבנות מחדש את האפליקציה כדי שהיא תפעל במכשירים האלה עם 16 KB.

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

כדי לעזור לכם להוסיף תמיכה לאפליקציה, סיפקנו הנחיות בנושא בדיקה אם יש השפעה על האפליקציה, בנייה מחדש של האפליקציה (אם רלוונטי) ובדיקת האפליקציה בסביבה של 16KB באמצעות אמולטורים (כולל תמונות מערכת של Android 15 לאמולטור של Android).

דרישת התאימות של Google Play

כדי לוודא שהאפליקציה פועלת בצורה תקינה בגרסאות העדכניות ביותר של Android, כל האפליקציות שמטרגטות ל-Android 15 (רמת API ‏35) ומעלה חייבות לתמוך בדפים עם זיכרון בגודל 16KB במכשירים עם 64 ביט ב-Google Play. החל מ-1 בפברואר 2027, אם העדכונים לאפליקציה לא יתמכו בדפים עם זיכרון בגודל 16KB, לא תהיה לך אפשרות לפרסם אותם.

אזהרה ב-Google Play Console: עדכוני אפליקציות חייבים לתמוך בדפי זיכרון בגודל 16KB עד 1 בפברואר 2027
תרשים 1. אזהרת תאימות של Google Play Console.

יתרונות ושיפורים בביצועים

במכשירים שמוגדרים בהם דפים בגודל 16KB, נעשה שימוש במעט יותר זיכרון בממוצע, אבל יש גם שיפורים שונים בביצועים של המערכת ושל האפליקציות:

  • זמני השקה קצרים יותר של אפליקציות בזמן שהמערכת נמצאת בלחץ על הזיכרון: נמוך ב-3.16% בממוצע, עם שיפורים משמעותיים יותר (עד 30%) בחלק מהאפליקציות שבדקנו
  • צריכת חשמל מופחתת במהלך השקת האפליקציה: הפחתה של 4.56% בממוצע
  • הפעלה מהירה יותר של המצלמה: הפעלה מתוך הזיכרון (hot start) מהירה יותר ב-4.48% בממוצע, והפעלה מחדש (cold start) מהירה יותר ב-6.60% בממוצע
  • זמן הפעלה משופר של המערכת: שיפור של 8% (כ-950 אלפיות השנייה) בממוצע

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

איך בודקים אם האפליקציה מושפעת מהשינוי

אם האפליקציה שלך משתמשת בקוד Native, עליך לבנות מחדש את האפליקציה עם תמיכה במכשירים עם דפי זיכרון בגודל 16 KB. אם אתם לא בטוחים אם האפליקציה שלכם משתמשת בקוד Native, אתם יכולים להשתמש בכלי APK Analyzer כדי לזהות אם יש קוד Native, ואז לבדוק את ההתאמה של פלחי ELF לכל הספריות המשותפות שמצאתם. ב-Android Studio יש גם תכונות שעוזרות לכם לזהות באופן אוטומטי בעיות שקשורות ליישור.

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

האם באפליקציה שלך נעשה שימוש בקוד Native?

האפליקציה משתמשת בקוד Native אם מתקיים אחד מהתנאים הבאים:

  • האפליקציה משתמשת בקוד C/C++‎ (קוד Native). אם האפליקציה משתמשת ב-Android NDK, היא משתמשת בקוד Native.
  • האפליקציה מקושרת לספריות Native או ליחסי תלות (כמו ערכות SDK) של צד שלישי שמשתמשים בהם.
  • האפליקציה שלך פותחה באמצעות כלי של צד שלישי לפיתוח אפליקציות, שמשתמש בספריות Native במכשיר.

זיהוי ספריות Native באמצעות הכלי לניתוח APK

הכלי לניתוח APK מאפשר להעריך היבטים שונים של קובץ APK שנוצר. כדי לבדוק אם האפליקציה משתמשת בקוד Native (גם אם היא תואמת ל-16KB):

  1. פותחים את Android Studio, לוחצים על File > Open (קובץ > פתיחה) ובוחרים פרויקט כלשהו.
  2. בסרגל התפריטים, לוחצים על Build > Analyze APK...‎ (פיתוח > ניתוח APK...‎).

    אפשרות בתפריט Build ב-Studio להפעלת הכלי לניתוח APK
  3. בוחרים את ה-APK שרוצים לנתח.

  4. מחפשים בתיקייה lib, שמכילה קובצי אובייקטים משותפים (.so) אם יש כאלה. אם יש קובצי אובייקטים משותפים, האפליקציה משתמשת בקוד Native. בעמודה Alignment מוצגות אזהרות לגבי קבצים שיש בהם בעיות תזמון. אם אין קובצי אובייקטים משותפים או שאין תיקיית lib, האפליקציה לא משתמשת בקוד Native.

    תצוגה של הכלי לניתוח APK שמראה שקיימים קובצי אובייקטים משותפים

זיהוי בעיות בהתאמה באמצעות בדיקות אוטומטיות

‫Android Studio מזהיר אתכם באופן יזום אם קובצי ה-APK או הספריות שנוצרו מראש לא תואמים ל-16KB. השתמשו בכלי לניתוח APK כדי לבדוק אילו ספריות צריך לעדכן או אם נדרשים שינויים בקוד.

התראות אזהרה ב-Studio לגבי בעיות בסנכרון בפרויקט

Lint ב-Android Studio גם מדגיש ספריות Native שלא מותאמות ל-16KB.

אזהרה של Studio linter לגבי ספריית Native לא מיושרת

בדיקת ההתאמה של קטעי ELF לספריות משותפות

בכל ספרייה משותפת, מוודאים שקטעי ה-ELF של הספרייה המשותפת מיושרים כמו שצריך באמצעות יישור ELF בגודל 16KB.

שימוש בכלי לניתוח APK (מומלץ)

הדרך הקלה ביותר לוודא שהספריות המשותפות מיושרות היא להשתמש בכלי לניתוח APK ב-Android Studio. אם קובץ אובייקט משותף לא מיושר ל-16KB, בעמודה Alignment תופיע הודעת אזהרה.

שימוש בסקריפט check_elf_alignment.sh (ב-Linux או ב-macOS)

אם אתם מפתחים ב-Linux או ב-macOS ומעדיפים להשתמש בסקריפט, אתם יכולים להשתמש בסקריפט check_elf_alignment.sh:

  1. שומרים את הסקריפט check_elf_alignment.sh בקובץ.

  2. מריצים את הסקריפט בקובץ ה-APK של האפליקציה:

    check_elf_alignment.sh APK_NAME.apk
    

    הסקריפט מפיק את הפלט ALIGNED או UNALIGNED לכל הספריות המשותפות של arm64-v8a.

  3. אם יש ספריות משותפות של arm64-v8a או x86_64 שהן UNALIGNED, צריך לעדכן את האריזה של הספריות האלה, ואז לקמפל מחדש את האפליקציה ולבדוק אותה שוב לפי השלבים בקטע הזה.

שימוש ישיר בכלי שורת הפקודה

כדי לבדוק את ההתאמה של מקטעי ELF באמצעות כלי שורת הפקודה, פועלים לפי השלבים הבאים:

  1. מוודאים שמותקנים גם Android SDK Build-Tools בגרסה 35.0.0 ואילך וגם Android NDK באמצעות SDK Manager ב-Android Studio או כלי שורת הפקודה sdkmanager.
  2. מחלצים את קובץ ה-APK של האפליקציה:

    Linux או macOS

    unzip APK_NAME.apk -d /tmp/my_apk_out
    

    ‏Windows (PowerShell)

    Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_out
    
  3. בספרייה הזמנית שחילצתם אליה את קובץ ה-APK, בודקים את התוכן של הספרייה lib כדי למצוא קובצי אובייקטים משותפים (.so). אלה אותם קובצי אובייקטים משותפים שהייתם רואים בזמן זיהוי ספריות Native באמצעות הכלי לניתוח APK. מריצים את הפקודה הבאה בכל קובץ אובייקט משותף:

    Linux או macOS

    SDK_ROOT_LOCATION/Android/sdk/ndk/NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump -p SHARED_OBJECT_FILE.so | grep LOAD
    

    ‏Windows (PowerShell)

    SDK_ROOT_LOCATION\Android\sdk\ndk\NDK_VERSION\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-objdump.exe -p SHARED_OBJECT_FILE.so | Select-String -Pattern "LOAD"
    

    כאשר SDK_ROOT_LOCATION הוא הנתיב לספרייה שבה התקנתם את Android SDK,‏ SHARED_OBJECT_FILE הוא השם של קובץ האובייקט המשותף שאתם בודקים ו-NDK_VERSION הוא הגרסה של Android NDK שהתקנתם (לדוגמה, 28.0.12433566). הפלט ייראה בערך כמו בדוגמה הבאה לכל קובץ שאתם בודקים:

    LOAD off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**14
    LOAD off    0x0000000000042a90 vaddr 0x0000000000043a90 paddr 0x0000000000043a90 align 2**14
    LOAD off    0x0000000000046230 vaddr 0x0000000000048230 paddr 0x0000000000048230 align 2**14
    
  4. בודקים את שורות הפלט כדי לוודא שאין ערכים קטנים מ-2**14 בקטעי הטעינה. אם יש קטעי טעינה עם הערכים 2**13, 2**12 או ערכים נמוכים יותר, תצטרכו לעדכן את האריזה של הספריות האלה, ואז לקמפל מחדש את האפליקציה ולבדוק אותה שוב לפי השלבים בקטע הזה.

  5. לאחר מכן, מריצים את כלי שורת הפקודה zipalign בקובץ ה-APK של האפליקציה:

    Linux או macOS

    SDK_ROOT_LOCATION/Android/sdk/build-tools/35.0.0/zipalign -v -c -P 16 4 APK_NAME.apk
    

    ‏Windows (PowerShell)

    SDK_ROOT_LOCATION\Android\sdk\build-tools\35.0.0\zipalign.exe -v -c -P 16 4 APK_NAME.apk
    

    SDK_ROOT_LOCATION הוא הנתיב לספרייה שבה התקנתם את Android SDK, ו-APK_NAME הוא השם של קובץ ה-APK של האפליקציה. אם כל הספריות המשותפות מיושרות בצורה נכונה, בשורה האחרונה של הפלט יופיע הכיתוב "Verification successful".

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

בדיקת דגל האבטחה של RELRO

כדי לצמצם את הסיכון לניצול פרצות אבטחה, מקשרים מודרניים משתמשים בדגל Relocation Read-Only (RELRO)‎ כדי להגדיר את קטעי היעד של ההעברה (כמו .got ו-.data.rel.ro) של קובץ האובייקט המשותף כקריאה בלבד אחרי שההעברות הדינמיות הושלמו. מפעילים את הדגל RELRO ב-build.

כש-GNU_RELRO משתף קטע LOAD שניתן לכתיבה עם נתונים שניתנים לכתיבה (כמו .data או .bss), כתובת הסיום של הקטע GNU_RELRO (VirtAddr + MemSiz) חייבת להיות מותאמת ל-16KB‏ (0x4000). אחרת, כשAndroid Linker מסמן את הדף בגודל 16KB שמכיל את הסוף של GNU_RELRO כדף לקריאה בלבד, כל הנתונים הסמוכים שניתנים לכתיבה ונמצאים בשאר הדף בגודל 16KB הופכים גם הם לנתונים לקריאה בלבד, והאפליקציה קורסת בזמן הריצה עם שגיאת חלוקה (SIGSEGV) כשמתבצעת כתיבה של הנתונים האלה. בדרך כלל זה קורה אם קובץ ה-.so נוצר באמצעות NDK toolchain r27 וגרסאות קודמות בלי להפעיל את הדגלים הרלוונטיים.

אתם יכולים להשתמש בכלי לניתוח APK ב-Android Studio כדי לבדוק ישירות את סטטוס ההתאמה של RELRO בקובצי האובייקטים המשותפים.

הכלי לניתוח APK ב-Android Studio מציג את היישור של RELRO לספריות Native

שימוש בכלי שורת הפקודה

אם אתם מעדיפים לבדוק את ההתאמה באופן ידני או צריכים להפוך את התהליך לאוטומטי, בצעו את השלבים הבאים:

שלב 1: בודקים את כותרות התוכנית

מריצים את הפקודה הבאה בכל קובץ אובייקט משותף (ב-Linux, מחליפים את darwin-x86_64 ב-linux-x86_64):

Linux או macOS

SDK_ROOT_LOCATION/Android/sdk/ndk/NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-readelf -Wl SHARED_OBJECT_FILE.so | grep 'LOAD\|RELRO\|Type'

‏Windows (PowerShell)

SDK_ROOT_LOCATION\Android\sdk\ndk\NDK_VERSION\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-readelf.exe -Wl SHARED_OBJECT_FILE.so | Select-String -Pattern 'LOAD|RELRO|Type'

המחרוזת GNU_RELRO מודפסת אם יש קטע RELRO.

שלב 2: בודקים אם RELRO תואם ל-16KB

קובץ אובייקט משותף תואם ל-16KB ביחס ל-RELRO אם מתקיים אחד מהתנאים הבאים:

  1. לא קיים פלח GNU_RELRO בכותרות של התוכנית.
  2. כתובת הסיום של GNU_RELRO מותאמת ל-16KB: הסכום של הכתובת הווירטואלית (VirtAddr) וגודל הזיכרון (MemSiz) חלקי 16KB ‏(0x4000) משאיר שארית (מודולו) של אפס: (VirtAddr + MemSiz) % 0x4000 == 0.
  3. אין נתונים שניתנים לכתיבה אחרי GNU_RELRO באותו קטע LOAD: ‫GNU_RELRO תופס את כל שאר הקטע LOAD שמכיל אותו (כלומר LOAD.VirtAddr + LOAD.MemSiz == RELRO.VirtAddr + RELRO.MemSiz), ולכן אין קטעי .data או .bss שניתנים לכתיבה שחולקים את הדף האחרון.

דוגמאות

בדוגמאות הבאות מוסבר איך להעריך את הפלט של llvm-readelf בהגדרות שונות של GNU_RELRO.

דוגמה 1: קובץ .so לא תואם (לא עומד בדרישות)

בדוגמה הזו, GNU_RELRO משתף קטע RW LOAD עם נתונים שניתנים לכתיבה, וכתובת הסיום שלו היא לא כפולה של 0x4000:

Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align
LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x0cfaf0 0x0cfaf0 R E 0x4000
LOAD           0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x02510 0x04510 RW  0x4000
GNU_RELRO      0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x01510 R   0x1
  • נוסחה: (VirtAddr + MemSiz) % 0x4000 == 0
  • התוצאה: (0xdfaf0 + 0x01510) % 0x4000 = 0xE1000 % 0x4000 == 0x1000

מכיוון שהערך של 0x1000 הוא לא אפס, והקטע LOAD שמכיל אותו ממשיך מעבר ל-0xE1000 (ומסתיים ב-0xdfaf0 + 0x04510 = 0xE4000), קובץ ה-.so הזה לא תואם ל-16KB. במכשיר 16KB, המקשר מעגל את טווח ההגנה של RELRO לגבולות דף של 16KB, ומסמן את 0xDC000 (מעבר הדף הקודם) עד 0xE4000 כ"לקריאה בלבד". עם זאת, הקטעים .data ו-.bss של הקובץ .so שניתנים לכתיבה נמצאים בטווח המשנה מ-0xE1000 עד 0xE4000, וכתוצאה מכך נוצרת שגיאת פילוח כשמשנים אותם בזמן הריצה. צריך לבנות מחדש את הקובץ .so כמו שמתואר בקטע הידור האפליקציה באמצעות יישור ELF בגודל 16KB.

דוגמה 2: קובץ .so תואם עם קטע RELRO (תואם)

בדוגמה הזו, הסוף של הקטע GNU_RELRO נמצא בגבול של דף בגודל 16KB:

Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align
LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x0cfaf0 0x0cfaf0 R E 0x4000
LOAD           0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x03510 RW  0x4000
GNU_RELRO      0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x00510 0x00510 R   0x1
  • נוסחה: (VirtAddr + MemSiz) % 0x4000 == 0
  • התוצאה: (0xdfaf0 + 0x00510) % 0x4000 = 0xE0000 % 0x4000 == 0

מכיוון שהשארית היא 0, ההגנה של RELRO מסתיימת באופן נקי בגבול הדף 0xE0000, וכל הנתונים שניתנים לכתיבה החל מ-0xE0000 לא מושפעים.

דוגמה 3: קובץ .so בלי קטע RELRO (תואם)
Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align
LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x000680 0x000680 R E 0x4000
LOAD           0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x0001d8 RW  0x4000

בקובץ .so הזה אין קטע GNU_RELRO, ולכן בעיות שקשורות לגבולות הדף של RELRO לא רלוונטיות (אבל עדיין מומלץ להפעיל את RELRO מטעמי אבטחה).

דוגמה 4: GNU_RELRO בפלח LOAD משלו (תואם)

בדוגמה הזו, GNU_RELRO לא מסתיים בגבול של 16KB, אבל הוא עדיין תואם כי אין אחריו נתונים שניתנים לכתיבה באותו קטע LOAD:

Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x000680 0x000680 R E 0x4000
LOAD           0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 RW  0x4000
GNU_RELRO      0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 R   0x1
  • נוסחה: (VirtAddr + MemSiz) % 0x4000 == 0
  • התוצאה: (0x4680 + 0x0980) % 0x4000 = 0x5000 % 0x4000 == 0x1000

למרות שהערך של 0x1000 הוא לא אפס, הקובץ .so עדיין תואם לדפים בגודל 16KB. אם משווים בין GNU_RELRO לבין הקטע LOAD שמכיל אותו (הרשומה השנייה של LOAD), רואים ששניהם מתחילים ב-VirtAddr 0x4680 ומסתיימים בדיוק באותה כתובת (0x4680 + 0x0980 = 0x5000).

מכיוון שכל הקטע LOAD מכיל רק נתוני RELRO, אין נתוני .data או .bss שניתנים לכתיבה בטווח המשנה מ-0x5000 עד 0x8000 (או שלספרייה אין נתונים שניתנים לכתיבה שאינם RELRO, או שכל קטע LOAD שניתן לכתיבה מתחיל בגבול דף נפרד של 16KB ב-0x8000 או מעבר לכך). ההגנה לקריאה בלבד של המקשר לא תחפוף לנתונים שניתן לכתוב, כך שלא תתרחש שגיאת פילוח.

איך מפתחים אפליקציה שתומכת במכשירים עם דפים בגודל 16KB

אם האפליקציה שלכם משתמשת בקוד Native, בצעו את השלבים שמפורטים בקטעים הבאים כדי לוודא שהאפליקציה תומכת במכשירים עם דפי זיכרון בגודל 16KB:

  1. עדכון האריזה של הספריות המשותפות
  2. הידור האפליקציה באמצעות יישור ELF בגודל 16KB
  3. תיקון קוד ופתרון בעיות בזמן ריצה
  4. אופטימיזציה של מקצי הזיכרון בהתאמה אישית (אם רלוונטי)
  5. בדיקה אם ערכות ה-SDK תומכות ב-16KB

עדכון האריזה של הספריות המשותפות

כדאי לשדרג לגרסה 8.5.1 ומעלה של AGP ולהשתמש בספריות משותפות לא דחוסות.

משתמשים ב-bundletool כדי לוודא שהרוכסן ישר

כדי לראות את היישור של החבילה, משתמשים בפקודה:

bundletool dump config --bundle=<my .aab>  | grep alignment

אם מופיע הערך PAGE_ALIGNMENT_16K, סימן שה-bundle מבקש יישור ZIP של 16KB. אם מופיע PAGE_ALIGNMENT_4K, זה אומר שקובץ ה-APK שנוצר מה-AAB הזה צריך להכיל קובצי .so מיושרים בגודל 4KB בקובץ ה-ZIP.

AGP בגרסה 8.5.1 ואילך

במכשירים עם 16KB, אפליקציות שכוללות ספריות משותפות לא דחוסות צריכות ליישר אותן לפי גבולות של 16KB ב-zip. כדי לעשות את זה, צריך לשדרג לגרסה 8.5.1 ומעלה של הפלאגין של Android Gradle ‏(AGP). בקטע מסייע השדרוג של הפלאגין של Android Gradle מוסבר איך משדרגים את הפלאגין.

AGP בגרסה 8.5 ומטה

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

מגניב

בקובץ build.gradle, מוסיפים את האפשרות הבאה:

android {
  ...
  packagingOptions {
      jniLibs {
        useLegacyPackaging true
      }
  }
}

Kotlin

בקובץ build.gradle.kts, מוסיפים את האפשרות הבאה:

android {
  ...
  packagingOptions {
      jniLibs {
        useLegacyPackaging = true
      }
  }
}
AGP בגרסה 8.0 ומטה

אם אתם משתמשים בגרסה 8.0 או בגרסה ישנה יותר של AGP, אתם צריכים גם להשבית את האפשרות של ספריית Native לא דחוסה לחבילות App Bundle בקובץ gradle.properties:

android.bundle.enableUncompressedNativeLibs=false

הידור האפליקציה באמצעות יישור ELF בגודל 16KB

כדי שהאפליקציה תפעל במכשירים עם דפי זיכרון בגודל 16KB, צריך ליישר את מקטעי ה-ELF של הספריות המשותפות באמצעות יישור ELF בגודל 16KB.

מפתחי משחקים שמשתמשים במנוע המשחק Unity יכולים לעיין במדריך של Unity. אם המשחק פועל על מנוע המשחק Unreal, עיינו במדריך של Unreal.אם מדובר במנועי משחק מקוריים, המשיכו לקרוא את המדריך הזה.

כדי להדר את האפליקציה באמצעות יישור ELF בגודל 16KB, צריך לבצע את השלבים באחד מהקטעים הבאים, בהתאם לגרסת Android NDK שבה אתם משתמשים.

‫Android NDK r28 ואילך

ב-NDK מגרסה r28 ואילך, הקומפילציה מיושרת כברירת מחדל ל-16KB.

‫Android NDK r27 וגרסאות קודמות

כדי לתמוך בהידור של ספריות משותפות שתואמות ל-16KB באמצעות Android NDK בגרסה r27 או גרסה ישנה יותר, צריך להשתמש בדגלי הקישור הבאים:

-Wl,-z,max-page-size=16384
-Wl,-z,common-page-size=16384

כך מעדכנים את קובצי התצורה של מערכת ה-build:

ndk-build

אם אתם משתמשים ב-ndk-build, עדכנו את Android.mk כדי להפעיל יישור ELF של 16KB:

LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384

CMake

אם אתם משתמשים ב-CMake, עדכנו את CMakeLists.txt כדי להפעיל יישור ELF של 16KB:

target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
    "-Wl,-z,max-page-size=16384"
    "-Wl,-z,common-page-size=16384"
)

תיקון קוד ופתרון בעיות בזמן ריצה

גם אם האפליקציה מותאמת ל-16KB, יכולות להיות בה שגיאות אם יש מקומות בקוד שבהם מניחים שהמכשיר משתמש בגודל דף ספציפי. כדי למנוע את זה, צריך לבצע את הפעולות הבאות:

  1. מסירים את כל יחסי התלות בתוך הקוד שמפנים לקבוע PAGE_SIZE או מופעים בלוגיקה של הקוד שמניחים שגודל הדף של המכשיר הוא 4KB‏ (4096).

    במקומה, צריך להשתמש ב-getpagesize() או ב-sysconf(_SC_PAGESIZE).

  2. חפשו שימושים ב-mmap() ובממשקי API אחרים שדורשים ארגומנטים מיושרים לדף, והחליפו אותם בחלופות לפי הצורך.

במקרים מסוימים, אם האפליקציה משתמשת ב-PAGE_SIZE כערך נוח שלא קשור לגודל הדף הבסיסי, האפליקציה לא תפסיק לפעול כשתשתמשו בה במצב 16KB. עם זאת, אם הערך הזה מועבר לליבה באמצעות mmap בלי MAP_FIXED, הליבה עדיין משתמשת בדף שלם, מה שגורם לבזבוז של חלק מהזיכרון. לכן, PAGE_SIZE לא מוגדר כשמצב 16KB מופעל ב-NDK r27 ואילך.

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

אופטימיזציה של מקציבי זיכרון מותאמים אישית

במערכות של 16KB, היחידה הקטנה ביותר של זיכרון פיזי שמערכת ההפעלה מקצה גדולה פי ארבעה מאשר במערכות של 4KB. אם מקצה זיכרון מותאם אישית תוכנן לפי הנחות של 4KB, הוא יכול לפזר אובייקטים קטנים על פני כמה דפים של 16KB ולשמור זיכרון ריק שלא לצורך. הפעולה הזו יכולה להגדיל באופן משמעותי את השימוש בזיכרון הפיזי (RSS) ולפגוע ביעילות של שטח ההחלפה הדחוס (ZRAM).

אם הקוד שלכם מנהל מאגרי זיכרון משלו, כדאי לפעול לפי ההמלצות הבאות:

1. הימנעו מספי בייטים בתוך הקוד לשחרור זיכרון

הרבה מקצים משתמשים במגבלות בייטים קבועות כדי להחליט מתי לשחרר זיכרון בחזרה למערכת ההפעלה באמצעות madvise(MADV_DONTNEED) (לדוגמה, לשחרר זיכרון רק אם אובייקטים פעילים תופסים פחות מ-8KB).

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

מה לעשות: אף פעם אל תשתמשו בקבועי בייטים בתוך הקוד בשביל היוריסטיקות של שחרור דפים. הגדלה דינמית של ספי השחרור בזמן הריצה על סמך גודל הדף בפועל באמצעות sysconf(_SC_PAGESIZE).

2. מילוי דפים שכבר נמצאים בשימוש (הקצאה לפי דחיסות)

אם המקצה מחלק זיכרון בסדר נכנס ראשון יוצא ראשון (FIFO) או בסבב, הקצאות חדשות מפוזרות על פני הרבה דפים של 16KB שמולאו באופן חלקי. אובייקט אחד בדף שומר את כל 16KB בזיכרון ה-RAM הפיזי.

מה לעשות: תמיד להקצות אובייקטים חדשים מהדף או מה-slab המלאים ביותר (הצפופים ביותר) לפני שנוגעים בדפים ריקים או בדפים שנעשה בהם שימוש מועט. ריכוז ההקצאות החדשות בדפים שכבר מלוכלכים מאפשר לדפים שנעשה בהם שימוש מועט להתרוקן באופן טבעי לאפס אובייקטים פעילים, כך שניתן לשחרר את כל הדף בגודל 16KB למערכת ההפעלה.

3. יישור מאגרי הזיכרון ל-16KB ושמירה על גודל מאגרים בינוני

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

מה לעשות:

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

4. שחרור מיידי של זיכרון פיזי למאגרים גדולים שנשמרו במטמון

מקציבים מותאמים אישית שומרים לעיתים קרובות מאגרים גדולים (‎> 64 KB) במטמון במאגר בזיכרון, כדי שיהיה אפשר להשתמש בהם שוב בלי לשלם את התקורה של קריאות המערכת mmap או munmap. עם זאת, שמירת המאגרים המלוכלכים האלה בזיכרון מבזבזת מגה-בייטים של זיכרון RAM פיזי בזמן ההמתנה לטיימר הפינוי.

מה לעשות: לשמור את טווח הכתובות של הזיכרון הווירטואלי לשימוש חוזר מהיר, אבל להפעיל את madvise(..., MADV_DONTNEED) או את madvise(..., MADV_FREE) מיד כשמחזירים מאגר למטמון. מערכת ההפעלה מחזירה את ה-RAM הפיזי מיד, והאפליקציה עדיין יכולה להשתמש שוב בכתובת הווירטואלית באופן מיידי בלי להקצות אותה מחדש.

5. איפוס הזיכרון בזמן שחרור במקום בזמן הקצאה לדחיסת ZRAM

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

אם המקצה מאפס את הזיכרון (למשל, למטרות אבטחה או הקצאות שמאופסות), כדאי לאפס בזמן ביטול ההקצאה (free()) ולא בזמן ההקצאה:

  • דפים מוצמדים עולים פחות: זיכרון לא פעיל בדפים בגודל 16KB שמלאים חלקית נדחס כמעט לאפס ב-ZRAM, כך שדפים מוצמדים לא מבזבזים שטח החלפה פיזי.
  • תקורה מינימלית של מטמון: בזמן הקריאה ל-free(), הזיכרון כבר חם במטמון של המעבד (CPU), וכך נמנעים מקרים נוספים של אי מציאה במטמון בהמשך.

סיכום ההמלצות

אזור המלצה ההשפעה הצפויה
ערכי סף לטיהור התאמה דינמית של ערכי הסף לפרסום באמצעות sysconf(_SC_PAGESIZE). מונעת מצב שבו לוגיקת השחרור נתקעת באופן סופי.
סדר ההקצאה קודם כל, מקצים מהדף או מהלוח הצפוף ביותר (כמעט מלא). מפחית את הזיכרון התושב (RSS) על ידי אריזת אובייקטים פעילים יחד.
גודל הטווח צריך להתאים את מחלקות הגודל לכפולות של 16KB ולהימנע משימוש ב-slabs גדולים מדי. מבטל את הפרגמנטציה של דפי הזנב ומצמצם את הזיכרון המבוזבז.
מאגרי מטמון צריך להפעיל את madvise(MADV_DONTNEED) מיד כששומרים מאגרים גדולים במטמון. התכונה הזו מונעת ניפוח של זיכרון RAM לא פעיל, תוך שמירה על שימוש חוזר וירטואלי מהיר.
Swap / ZRAM איפוס הזיכרון ב-free() במקום בהקצאה. משפר את יחסי הדחיסה של ZRAM, כך שדפים מוצמדים צורכים פחות משאבים.

בדיקה אם ערכות ה-SDK תומכות ב-16KB

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

בדיקת האפליקציה בסביבה של 16KB

אחרי שיוצרים אפליקציה עם תמיכה במכשירים עם דפים בגודל 16KB, צריך לבדוק את חוויית השימוש באפליקציה בסביבה של 16KB כדי לראות אם יש בה רגרסיות. לשם כך, בצע את הצעדים הבאים:

  1. מגדירים את Android 15 SDK ואילך.

  2. מגדירים את אחת מסביבות הבדיקה הבאות:

  3. מפעילים את מכשיר הבדיקה ומריצים את הפקודה הבאה כדי לוודא שהוא משתמש בסביבה של 16KB:

    adb shell getconf PAGE_SIZE
    

    הפקודה אמורה להחזיר את הערך 16384.

  4. מריצים את הפקודה הבאה של zipalign כדי לוודא שהאפליקציה מותאמת ל-16KB, כאשר APK_NAME הוא השם של קובץ ה-APK של האפליקציה:

    zipalign -c -P 16 -v 4 APK_NAME.apk
    
  5. בודקים את האפליקציה ביסודיות, ומתמקדים באזורים שעשויים להיות מושפעים משינוי מופעי קוד שמפנים לגדלים ספציפיים של דפים.

הגדרת אמולטור Android עם קובץ אימג' של מערכת שמבוססת על 16KB

כדי להגדיר סביבה של 16KB באמצעות אמולטור Android, פועלים לפי השלבים הבאים:

  1. ב-Android Studio, לוחצים על Tools > ‏SDK Manager (כלים > SDK Manager).
  2. בכרטיסייה SDK Platforms, בוחרים באפשרות Show Package Details, מרחיבים את הקטע Android VanillaIceCream או גרסה חדשה יותר ובוחרים אחת או שתיים מתמונות המערכת הבאות של האמולטור, בהתאם למכשירים הווירטואליים שרוצים ליצור:

    • תמונת מערכת של Google APIs Experimental 16 KB Page Size ARM 64 v8a
    • Google APIs Experimental 16 KB Page Size Intel x86_64 Atom System Image
    הורדת קובצי אימג' של מערכת האמולטור בגודל 16KB באמצעות SDK Manager ב-Android Studio
  3. לוחצים על Apply > OK כדי להוריד את קובצי האימג' של המערכת שבחרתם.

  4. פועלים לפי השלבים להגדרת מכשיר וירטואלי ל-Android 15, וכשמוצגת בקשה לבחור קובץ אימג' של המערכת, בוחרים את קובץ האימג' של המערכת בגודל 16KB שהורדתם. אם היא לא מומלצת באופן אוטומטי, אפשר למצוא את קובץ האימג' של המערכת בגודל 16KB בכרטיסייה Other Images.

    מאתרים את קובץ האימג' של האמולטור בגודל 16KB בכרטיסייה Other Images (קובצי אימג' אחרים)

הפעלת האמולטור

אחרי שמסיימים להגדיר את אמולטור Android ואת המכשירים הווירטואליים, מפעילים את האמולטור מתפריט מכשיר היעד או משורת הפקודה.

הפעלת מצב 16 KB במכשיר באמצעות אפשרויות למפתחים

כדי להפעיל מכשיר במצב 16KB, מעבירים את המתג של אפשרות המפתחים הפעלה בגודל דף של 16KB.

בגרסאות QPR של Android 15, אפשר להשתמש באפשרות למפתחים שזמינה במכשירים מסוימים כדי להפעיל את המכשיר במצב 16KB ולבצע בדיקות במכשיר. לפני שמשתמשים באפשרות למפתחים, עוברים אל הגדרות > מערכת > עדכוני תוכנה ומחילים את כל העדכונים שזמינים.

אפשרות למפתחים זו זמינה במכשירים הבאים:

  • ‫Pixel 8 ו-Pixel 8 Pro (עם Android 15 QPR1 ומעלה)

  • ‫Pixel 8a (עם Android 15 QPR1 ומעלה)

  • ‫Pixel 9,‏ Pixel 9 Pro ו-Pixel 9 Pro XL (עם Android 15 QPR2 ומעלה)

  • ‫Pixel 9a (עם Android מגרסה 16 ואילך)

מצב תאימות לאחור של 16KB

אזהרה במצב של תאימות לגודל הדף

אזהרה במצב תאימות לגודל הדף

האפשרות לתאימות לאחור של 16KB זמינה כשהמכשיר פועל עם ליבה של 16KB. מנהל החבילות מפעיל אפליקציה במצב תאימות לאחור של 16KB כשהתנאים הבאים מתקיימים:

  • אם באפליקציה יש קובצי ELF (עם סיומת .so) עם יישור של מקטע LOAD בגודל 4KB.
  • אם קובץ ה-APK הדחוס מכיל קובצי ELF לא דחוסים שמותאמים ל-ZIP בגודל 4KB.

אם מנהל החבילות הפעיל את מצב התאימות לאחור של 16KB באפליקציה, האפליקציה תציג אזהרה כשהיא תופעל בפעם הראשונה, ותציין שהיא פועלת במצב תאימות לאחור של 16KB.

מצב תאימות לאחור של 16KB מאפשר לאפליקציות מסוימות לפעול, אבל כדי שהאפליקציות יהיו אמינות ויציבות ככל האפשר, הן עדיין צריכות להיות מיושרות ל-16KB.

בדף המידע על האפליקציה, בקטע מתקדם, מפעילים או משביתים את ההגדרה הפעלת האפליקציה במצב של תאימות לגודל הדף כדי להפעיל או להשבית את מצב התאימות לאחור של 16KB באפליקציה ספציפית. ההגדרה הזו גלויה רק כשהמכשיר פועל עם גודל דף של 16KB.

הגדרת מצב תאימות לגודל הדף

הגדרת מצב תאימות לגודל הדף

כדי לאלץ תאימות לאחור של 16KB בכל אפליקציה במכשיר:

adb shell setprop bionic.linker.16kb.app_compat.enabled true
adb shell setprop pm.16kb.app_compat.disabled false

כדי להשבית את התאימות לאחור של 16KB בכל האפליקציות במכשיר:

adb shell setprop bionic.linker.16kb.app_compat.enabled false
adb shell setprop pm.16kb.app_compat.disabled true

ב-Android 17, אתם יכולים גם להשבית את התאימות לאחור של 16KB לכל אפליקציה, ולגרום לכל קובץ בינארי לא תואם להפסיק לפעול באופן מיידי:

    adb shell setprop bionic.linker.16kb.app_compat.enabled fatal
    adb shell setprop pm.16kb.app_compat.disabled true

כדי להפעיל או להשבית את מצב התאימות לאחור באפליקציה ספציפית, מגדירים את המאפיין android:pageSizeCompat כ"מופעל" או "מושבת" בקובץ AndroidManifest.xml של האפליקציה. כשהמאפיין הזה מוגדר, האפליקציה לא תציג אזהרות לגבי מצב תאימות לאחור כשהיא מופעלת.