في السابق، كان نظام التشغيل Android يتيح فقط صفحات الذاكرة بحجم 4 كيلوبايت، ما ساعد في تحسين أداء ذاكرة النظام بما يتناسب مع إجمالي مساحة الذاكرة التي كانت تتوفّر عادةً في أجهزة Android. بدءًا من الإصدار 15 من نظام التشغيل Android، يتيح مشروع Android مفتوح المصدر (AOSP) استخدام الأجهزة التي تم ضبطها لاستخدام حجم صفحة يبلغ 16 كيلوبايت (أجهزة 16 كيلوبايت). إذا كان تطبيقك يستخدم أي مكتبات NDK، سواء بشكل مباشر أو غير مباشر من خلال حزمة تطوير برامج (SDK)، عليك إعادة إنشاء تطبيقك لكي يعمل على هذه الأجهزة التي تستخدم صفحات ذاكرة بحجم 16 كيلوبايت.
مع استمرار الشركات المصنّعة للأجهزة في تصميم أجهزة تتضمّن كميات أكبر من الذاكرة الفعلية (RAM)، ستستخدم العديد من هذه الأجهزة أحجام صفحات تبلغ 16 كيلوبايت (وأكبر في النهاية) لتحسين أداء الجهاز. تتيح إضافة دعم للأجهزة التي تستخدم صفحات بحجم 16 كيلوبايت تشغيل تطبيقك على هذه الأجهزة، كما تساعد تطبيقك في الاستفادة من التحسينات المرتبطة بالأداء. وبدون إعادة تجميع، لن تعمل التطبيقات على الأجهزة التي تستخدم صفحات الذاكرة بحجم 16 كيلوبايت في إصدارات Android المستقبلية.
لمساعدتك في إضافة إمكانية استخدام تطبيقك، قدّمنا إرشادات حول كيفية التحقّق مما إذا كان تطبيقك سيتأثر، وكيفية إعادة إنشاء تطبيقك (إذا كان ذلك منطبقًا)، وكيفية اختبار تطبيقك في بيئة بحجم 16 كيلوبايت باستخدام المحاكيات (بما في ذلك صور نظام Android 15 لمحاكي Android).
متطلبات التوافق مع Google Play
لضمان عمل تطبيقك بشكل صحيح على أحدث إصدارات Android، يجب أن تتوافق جميع التطبيقات التي تستهدف الإصدار Android 15 (مستوى واجهة برمجة التطبيقات 35) والإصدارات الأحدث مع صفحات الذاكرة بحجم 16 كيلوبايت على الأجهزة التي تعمل بمعالجات 64 بت على Google Play. اعتبارًا من 1 فبراير 2027، إذا كانت تحديثات تطبيقك لا تتوافق مع صفحات الذاكرة بحجم 16 كيلوبايت، لن يكون بإمكانك طرح هذه التحديثات.
المزايا وتحسين الأداء
تستهلك الأجهزة التي تم ضبطها على أحجام صفحات تبلغ 16 كيلوبايت مساحة أكبر قليلاً من الذاكرة في المتوسط، لكنها تُجري أيضًا تحسينات متنوعة في الأداء لكل من النظام والتطبيقات:
- أوقات تشغيل التطبيق أقل عندما يكون النظام تحت ضغط الذاكرة: 3.16% انخفاضًا في المتوسط، مع تحسينات أكثر أهمية (تصل إلى %30) لبعض التطبيقات التي اختبرناها
- انخفاض في استهلاك الطاقة أثناء تشغيل التطبيق: انخفاض بنسبة% 4.56 في المتوسّط
- تشغيل أسرع للكاميرا: عمليات تشغيل أسرع بنسبة 4.48% في المتوسط، وعمليات تشغيل على البارد أسرع بنسبة 6.60% في المتوسط
- مدة تشغيل النظام المحسَّنة: تحسّنت بنسبة %8 (950 ملي ثانية تقريبًا) في المتوسّط
تستند هذه التحسينات إلى اختبارنا الأوّلي، ومن المرجّح أن تختلف النتائج على الأجهزة الفعلية. وسنقدّم تحليلاً إضافيًا للفوائد المحتملة للتطبيقات أثناء مواصلة الاختبار.
التحقّق مما إذا كان تطبيقك متأثرًا
إذا كان تطبيقك يستخدم أي رموز برمجية أصلية، عليك إعادة إنشاء تطبيقك ليتوافق مع الأجهزة التي تستخدم صفحات ذاكرة بحجم 16 كيلوبايت. إذا لم تكن متأكدًا مما إذا كان تطبيقك يستخدم رمزًا برمجيًا أصليًا، يمكنك استخدام "أداة تحليل حِزم APK" لتحديد ما إذا كان هناك أي رمز برمجي أصلي، ثم التحقّق من توافق أقسام ELF مع أي مكتبات مشتركة تجدها. يوفّر "استوديو Android" أيضًا ميزات تساعدك في رصد مشاكل المحاذاة تلقائيًا.
إذا كان تطبيقك يستخدم فقط الرموز البرمجية المكتوبة بلغة البرمجة Java أو Kotlin، بما في ذلك جميع المكتبات أو حِزم تطوير البرامج (SDK)، يكون تطبيقك متوافقًا مع الأجهزة التي تبلغ سعتها 16 كيلوبايت. ومع ذلك، ننصحك باختبار تطبيقك في بيئة بحجم 16 كيلوبايت للتأكّد من عدم حدوث أي تراجع غير متوقّع في سلوك التطبيق.
هل يستخدم تطبيقك رموزًا برمجية أصلية؟
يستخدم تطبيقك الرموز البرمجية الأصلية في أيّ من الحالات التالية:
- يستخدم تطبيقك أي رمز برمجي C/C++ (أصلي). إذا كان تطبيقك يستخدم حزمة تطوير البرامج الأصلية (NDK) لنظام Android، فهذا يعني أنّه يستخدم الرمز البرمجي الأصلي.
- يرتبط تطبيقك بأي مكتبات مجمّعة من رموز برمجية أصلية أو تبعيات تابعة لجهات خارجية (مثل حِزم تطوير البرامج) تستخدمها.
- تم إنشاء تطبيقك باستخدام أداة إنشاء تطبيقات تابعة لجهة خارجية تستخدم مكتبات مجمّعة من رموز برمجية أصلية على الجهاز.
تحديد المكتبات المجمّعة من رموز برمجية أصلية باستخدام "أداة تحليل ملفات APK"
أداة تحليل ملفات APK هي أداة تتيح لك تقييم جوانب مختلفة من حِزمة APK تم إنشاؤها. للتحقّق ممّا إذا كان تطبيقك يستخدم رموزًا برمجية أصلية (بغض النظر عمّا إذا كانت متوافقة مع الصفحات بحجم 16 كيلوبايت):
- افتح استوديو Android، ثم انقر على ملف > فتح واختَر أي مشروع.
من شريط القوائم، انقر على Build (إنشاء) > Analyze APK... (تحليل حزمة APK...)
اختَر حزمة APK التي تريد تحليلها.
ابحث في المجلد
libالذي يستضيف ملفات العناصر المشتركة (.so) في حال توفّرها. إذا كان هناك أي ملفات كائنات مشتركة، فإن تطبيقك يستخدم رموزًا برمجية أصلية. يعرض عمود المحاذاة رسائل تحذيرية لأي ملفات بها مشاكل في المحاذاة. إذا لم تكن هناك ملفات عناصر مشتركة أو لم يكن هناك مجلدlib، يعني ذلك أنّ تطبيقك لا يستخدم رموزًا برمجية أصلية.
رصد مشاكل المحاذاة من خلال عمليات التحقّق الآلية
يُرسل إليك "استوديو Android" تحذيرات استباقية إذا كانت المكتبات أو حِزم APK المُنشأة مسبقًا غير متوافقة مع حجم الصفحة الذي يبلغ 16 كيلوبايت. استخدِم أداة تحليل ملفات APK لمراجعة المكتبات التي تحتاج إلى تحديث أو لمعرفة ما إذا كانت هناك حاجة إلى إجراء أي تغييرات على الرمز البرمجي.
تُبرز أداة lint في "استوديو Android" أيضًا المكتبات المجمّعة من رموز برمجية أصلية التي لم تتم محاذاتها مع صفحة الذاكرة بحجم 16 كيلوبايت.
التحقّق من محاذاة أقسام ELF للمكتبات المشترَكة
بالنسبة إلى أي مكتبات مشتركة، تأكَّد من أنّ أجزاء ELF الخاصة بالمكتبات المشتركة
تتم محاذاتها بشكل صحيح باستخدام محاذاة ELF بحجم 16 كيلوبايت. إذا كنت تطوّر على نظام التشغيل Linux أو macOS، يمكنك استخدام النص البرمجي check_elf_alignment.sh كما هو موضّح في القسم التالي. يمكنك أيضًا استخدام أدوات سطر الأوامر مباشرةً.
استخدام النص البرمجي check_elf_alignment.sh (نظام التشغيل Linux أو macOS)
اتّبِع الخطوات التالية للتحقّق من محاذاة أجزاء ELF باستخدام النص البرمجي check_elf_alignment.sh:
احفظ النص البرمجي
check_elf_alignment.shفي ملف.شغِّل النص البرمجي على ملف APK الخاص بتطبيقك:
check_elf_alignment.sh APK_NAME.apkيُخرج النص البرمجي إما
ALIGNEDأوUNALIGNEDلجميع المكتبات المشتركة فيarm64-v8a.إذا كانت أي مكتبات مشتركة من النوع
arm64-v8aأوx86_64UNALIGNED، عليك تحديث حزمة هذه المكتبات، ثم إعادة تجميع تطبيقك وإعادة اختباره باتّباع الخطوات الواردة في هذا القسم.
استخدام أدوات سطر الأوامر مباشرةً
اتّبِع الخطوات التالية للتحقّق من محاذاة أجزاء ELF باستخدام أدوات سطر الأوامر مباشرةً:
- تأكَّد من تثبيت الإصدار 35.0.0 أو إصدار أحدث من أدوات إنشاء حزمة تطوير البرامج (SDK) لنظام التشغيل Android وحزمة Android NDK باستخدام SDK Manager في "استوديو Android" أو أداة سطر الأوامر
sdkmanager. استخرِج ملف 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في الدليل المؤقت الذي استخرجت ملف APK إليه، تحقَّق من محتوى الدليل
libبحثًا عن ملفات العناصر المشتركة (.so). وهي ملفات الكائنات المشترَكة نفسها التي كنت ستراها أثناء تحديد المكتبات المجمّعة من رموز برمجية أصلية باستخدام أداة تحليل ملفات 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هو المسار إلى الدليل الذي ثبّتّ فيه حزمة تطوير البرامج (SDK) لنظام التشغيل Android، و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راجِع أسطر الإخراج للتأكّد من أنّ مقاطع التحميل لا تحتوي على قيم أقل من
2**14. إذا كانت أي من شرائح التحميل2**13أو2**12أو قيم أقل، ستحتاج إلى تحديث حزمة هذه المكتبات، ثم إعادة تجميع تطبيقك وإعادة اختباره باتّباع الخطوات الواردة في هذا القسم.بعد ذلك، شغِّل أداة سطر الأوامر
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مسار الدليل الذي ثبّتّ فيه حزمة تطوير البرامج (SDK) لنظام التشغيل Android، ويمثّلAPK_NAMEاسم ملف APK الخاص بتطبيقك. سيظهر في السطر الأخير من الناتج "Verification successful" (تم التحقق بنجاح) إذا تمت محاذاة جميع المكتبات المشتركة بشكل صحيح.إذا تعذّر التحقّق، يجب إعادة تنظيم بعض المكتبات المشتركة، لذا عليك تحديث حزمة هذه المكتبات، ثم إعادة تجميع تطبيقك وإعادة اختباره باتّباع الخطوات الواردة في هذا القسم.
التحقّق من علامة أمان RELRO
للحدّ من الثغرات الأمنية، تستخدم برامج الربط الحديثة علامة Relocation Read-Only (RELRO) لجعل أقسام إعادة التوطين في ملف الكائن المشترك للقراءة فقط بعد التحميل. فعِّل علامة RELRO في إصدارك.
يؤدي قسم RELRO الذي يتضمّن عنوان بدء بالإضافة إلى حجم القسم (MemSize) غير المحاذي إلى 16 كيلوبايت إلى تعطُّل التطبيق في وقت التشغيل بسبب خطأ تجزئة. يحدث ذلك إذا تم إنشاء ملف .so باستخدام سلسلة أدوات NDK بالإصدار r27 أو إصدار أقدم بدون تفعيل العلامات ذات الصلة.
نفِّذ الأمر التالي على كل ملف كائن مشترك (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 'RELRO\|Type'
تتم طباعة السلسلة GNU_RELRO في حال توفّر قسم RELRO.
بعد ذلك، تحقَّق من محاذاة قسم RELRO من خلال جمع عنوان الإزاحة الافتراضي (VirtAddr) مع حجم ذاكرة القسم (MemSiz) والقسمة على 16 كيلوبايت (0x4000). إذا كان الباقي (الناتج عن عملية حسابية) صفرًا، يعني ذلك أنّ قسم RELRO متوافق مع حجم 16 كيلوبايت.
في ما يلي مثال على ملف .so غير متوافق:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x01510 R 0x1
الصيغة: (VirtAddr + MemSiz) % 0x4000 == 0
النتيجة: (0xdfaf0 + 0x01510) % 0x4000 = 0xE1000 % 0x4000 == 0x1000
بما أنّ القيمة 0x1000 ليست صفرًا، فإنّ ملف .so هذا غير متوافق مع الصفحة بحجم 16 كيلوبايت. نطاق حماية RELRO من DC000 (فاصل الصفحة السابق) إلى E4000 هو للقراءة فقط، ولكن Android Linker يتوقع أن يكون النطاق الفرعي من E1000 إلى E4000 قابلاً للكتابة، ما يؤدي إلى حدوث خطأ في التقسيم. في هذه الحالة، أعِد إنشاء ملف .so
كما هو موضّح في قسم
تجميع تطبيقك باستخدام محاذاة ELF بحجم 16 كيلوبايت.
في ما يلي مثال على ملف .so متوافق مع شريحة RELRO:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x00510 R 0x1
في ما يلي مثال على ملف .so بدون قسم RELRO:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
هذا ملف .so متوافق مع صفحة الذاكرة بحجم 16 كيلوبايت بدون أي جزء من RELRO.
في ما يلي مثال على ملف .so متوافق حيث يكون GNU_RELRO في قسم 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 هذا لا يزال متوافقًا مع صفحة الذاكرة بحجم 16 كيلوبايت.
يتم تطبيق صيغة القسمة النمطية عندما تشارك GNU_RELRO جزءًا من LOAD مع بيانات قابلة للكتابة لاحقة (مثل .data أو .bss).
لمعرفة ما إذا كان GNU_RELRO في مقطع LOAD خاص به، افحص عناوين البرنامج من خلال تشغيل llvm-readelf -Wl والفلترة باستخدام grep 'LOAD\|RELRO\|Type'. قارِن بين VirtAddr وMemSiz في
GNU_RELRO مقابل شريحة LOAD التي تتضمّنها. في هذا المثال،
يحتوي كلاهما على VirtAddr 0x4680 وMemSiz 0x0980.
بما أنّ شريحة LOAD بالكامل تحتوي على بيانات RELRO فقط، لا تتوفّر
بيانات قابلة للكتابة في النطاق الفرعي من 0x5000 إلى 0x8000. بالإضافة إلى ذلك، يبدأ أي جزء LOAD قابل للكتابة بعد ذلك على حد صفحة منفصلة بحجم 16 كيلوبايت (عند 0x8000 أو أكثر). لن تتداخل حماية الرابط للقراءة فقط مع أي بيانات قابلة للكتابة، لذلك لن يحدث خطأ في التجزئة.
إنشاء تطبيقك ليتوافق مع الأجهزة التي تستخدم صفحات الذاكرة بحجم 16 كيلوبايت
إذا كان تطبيقك يستخدم رموزًا برمجية أصلية، يُرجى إكمال الخطوات الموضّحة في الأقسام التالية للتأكّد من أنّ تطبيقك متوافق مع الأجهزة التي تستخدم صفحات ذاكرة بحجم 16 كيلوبايت:
- تحديث حِزم المكتبات المشتركة
- تجميع تطبيقك باستخدام محاذاة ELF بحجم 16 كيلوبايت
- إصلاح الرموز البرمجية وحلّ مشاكل وقت التشغيل
- تحسين أدوات تخصيص الذاكرة المخصّصة (إن وُجدت)
- التحقّق من توافق حزم تطوير البرامج (SDK) مع صفحة الذاكرة بحجم 16 كيلوبايت
تعديل حِزم المكتبات المشتركة
ننصحك بالترقية إلى الإصدار 8.5.1 أو إصدار أحدث من المكوّن الإضافي لنظام Gradle المتوافق مع Android (AGP) واستخدام المكتبات المشتركة غير المضغوطة.
استخدام bundletool للتحقّق من محاذاة السحّاب
للاطّلاع على محاذاة الحزمة، استخدِم:
bundletool dump config --bundle=<my .aab> | grep alignment
إذا ظهر لك PAGE_ALIGNMENT_16K، يعني ذلك أنّ حزمة تطبيقك تطلب محاذاة ملفات ZIP بحجم 16 كيلوبايت. إذا رأيت PAGE_ALIGNMENT_4K، يعني ذلك أنّ حزمة APK التي تم إنشاؤها من تنسيق AAB هذا يجب أن تتضمّن ملفات .so متوافقة مع 4 كيلوبايت في ملف ZIP.
الإصدار 8.5.1 من "المكوّن الإضافي لنظام Gradle المتوافق مع Android" أو إصدار أحدث
تتطلّب الأجهزة التي تستخدم صفحات ذاكرة بحجم 16 كيلوبايت أن تتم محاذاة التطبيقات التي تتضمّن مكتبات مشتركة غير مضغوطة على حدود ملفات zip بحجم 16 كيلوبايت. لإجراء ذلك، عليك الترقية إلى الإصدار 8.5.1 أو إصدار أحدث من "المكوّن الإضافي لنظام Gradle المتوافق مع Android" (AGP). راجِع القسم أداة المساعدة في ترقية المكوّن الإضافي لنظام Gradle المتوافق مع Android للاطّلاع على تفاصيل حول عملية الترقية.
الإصدار 8.5 من "المكوّن الإضافي لنظام Gradle المتوافق مع Android" أو إصدار أقدم
إذا لم تتمكّن من ترقية AGP إلى الإصدار 8.5.1 أو إصدار أحدث، يمكنك بدلاً من ذلك التبديل إلى استخدام المكتبات المشتركة المضغوطة. يُرجى تعديل إعدادات Gradle ليضغط Gradle المكتبات المشتركة عند حزم تطبيقك لتجنُّب مشاكل تثبيت التطبيق بسبب المكتبات المشتركة غير المتوافقة.
أنيق
في ملف build.gradle، أضِف الخيار التالي:
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
}
Kotlin
في ملف build.gradle.kts، أضِف الخيار التالي:
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
}
الإصدار 8.0 أو إصدار أقدم من "مكوّن Android الإضافي"
إذا كنت تستخدم إصدارًا من "المكوّن الإضافي لنظام Gradle في Android" (AGP) يساوي 8.0 أو أقدم، عليك أيضًا إيقاف
خيار المكتبة غير المضغوطة المجمّعة من رموز برمجية أصلية لحِزم التطبيقات في
ملف gradle.properties:
android.bundle.enableUncompressedNativeLibs=false
تجميع تطبيقك باستخدام محاذاة ELF بحجم 16 كيلوبايت
تتطلّب الأجهزة التي تستخدم صفحات ذاكرة بحجم 16 كيلوبايت محاذاة أجزاء ELF في المكتبات المشتركة بشكل صحيح باستخدام محاذاة ELF بحجم 16 كيلوبايت لكي يتم تشغيل تطبيقك.
بالنسبة إلى مطوّري الألعاب، إذا كانت لعبتك تعمل على محرّك ألعاب Unity، يُرجى الرجوع إلى دليل Unity. إذا كانت لعبتك تعمل على محرّك ألعاب Unreal، يُرجى الرجوع إلى دليل Unreal.أما إذا كنت تستخدم محرّكات ألعاب أصلية، فتابِع قراءة هذا الدليل.
لتجميع تطبيقك باستخدام محاذاة ELF بحجم 16 كيلوبايت، أكمِل الخطوات الواردة في أحد الأقسام التالية حسب إصدار Android NDK الذي تستخدمه.
Android NDK الإصدار 28 والإصدارات الأحدث
يتم تجميع الإصدار r28 من NDK والإصدارات الأحدث بمحاذاة 16 كيلوبايت تلقائيًا.
الإصدار r27 من Android NDK والإصدارات الأقدم
لإتاحة تجميع المكتبات المشترَكة المتوافقة مع 16 كيلوبايت باستخدام الإصدار 27 أو إصدار أقدم من Android NDK، استخدِم علامات الرابط التالية:
-Wl,-z,max-page-size=16384
-Wl,-z,common-page-size=16384
في ما يلي كيفية تعديل ملفات إعداد نظام الإصدار:
ndk-build
إذا كنت تستخدم ndk-build، عدِّل Android.mk لتفعيل محاذاة ملفات ELF بحجم 16 كيلوبايت:
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384
CMake
إذا كنت تستخدم CMake، يُرجى تعديل CMakeLists.txt لتفعيل محاذاة ELF بحجم 16 كيلوبايت:
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
"-Wl,-z,max-page-size=16384"
"-Wl,-z,common-page-size=16384"
)
إصلاح الرموز البرمجية وحلّ مشاكل وقت التشغيل
حتى إذا كان تطبيقك متوافقًا مع صفحة الذاكرة بحجم 16 كيلوبايت، قد يواجه أخطاء إذا افترضت بعض أجزاء الرمز البرمجي أنّ الجهاز يستخدم حجم صفحة معيّنًا. لتجنُّب ذلك، أكمل الخطوات التالية:
أزِل أي تبعيات مبرمَجة بشكل ثابت تشير إلى الثابت
PAGE_SIZEأو أي مثيلات في منطق الرمز البرمجي تفترض أنّ حجم الصفحة على الجهاز هو 4 كيلوبايت (4096).يُرجى استخدام
getpagesize()أوsysconf(_SC_PAGESIZE)بدلاً من ذلك.ابحث عن استخدامات
mmap()وواجهات برمجة التطبيقات الأخرى التي تتطلّب وسيطات متوافقة مع الصفحة واستبدِلها ببدائل عند الضرورة.
في بعض الحالات، إذا كان تطبيقك يستخدم PAGE_SIZE كقيمة ملائمة غير مرتبطة بحجم الصفحة الأساسي، فلن يؤدي ذلك إلى تعطُّل تطبيقك عند استخدامه في وضع 16 كيلوبايت. ومع ذلك، إذا تم تمرير هذه القيمة إلى النواة باستخدام
mmap بدون MAP_FIXED، ستظل النواة تستخدم صفحة كاملة، ما يؤدي إلى إهدار
بعض الذاكرة. لهذه الأسباب، لا يتم تحديد PAGE_SIZE عند تفعيل وضع 16 كيلوبايت في الإصدار r27 من NDK والإصدارات الأحدث.
إذا كان تطبيقك يستخدم PAGE_SIZE بهذه الطريقة ولا يمرّر هذه القيمة مباشرةً إلى النواة، فبدلاً من استخدام PAGE_SIZE، أنشئ متغيّرًا جديدًا باسم جديد ليعكس أنّه يُستخدم لأغراض أخرى ولا يعكس صفحة ذاكرة حقيقية.
تحسين أدوات تخصيص الذاكرة المخصّصة
في الأنظمة التي تستخدم صفحات بحجم 16 كيلوبايت، تكون أصغر وحدة من الذاكرة الفعلية التي يخصّصها نظام التشغيل أكبر بأربع مرات من الأنظمة التي تستخدم صفحات بحجم 4 كيلوبايت. إذا تم تصميم أداة تخصيص مخصّصة استنادًا إلى افتراضات حجم 4 كيلوبايت، يمكن أن يؤدي ذلك إلى توزيع العناصر الصغيرة على صفحات متعددة بحجم 16 كيلوبايت والاحتفاظ بذاكرة فارغة بدون داعٍ. ويمكن أن يؤدي ذلك إلى زيادة كبيرة في استخدام الذاكرة الفعلية (RSS) وتقليل كفاءة مساحة الإبدال المضغوطة (ZRAM).
إذا كان الرمز البرمجي يدير مجموعات الذاكرة الخاصة به، اتّبِع الاقتراحات التالية:
1. تجنُّب استخدام حدود البايت غير القابلة للتغيير في البرنامج لإخلاء الذاكرة
يستخدم العديد من المخصِّصات حدودًا ثابتة للبايت لتحديد وقت إعادة الذاكرة إلى نظام التشغيل باستخدام madvise(MADV_DONTNEED) (على سبيل المثال، لا تتم إعادة الذاكرة إلا إذا كانت العناصر النشطة تشغل أقل من 8 كيلوبايت).
على نظام بحجم 16 كيلوبايت، حتى كائن واحد نشط بحجم 16 بايت يثبّت صفحة كاملة بحجم 16 كيلوبايت (وهو أكبر من 8 كيلوبايت). ونتيجةً لذلك، لا يتم بلوغ الحدّ الأدنى للإصدار مطلقًا، ولا يعيد المخصِّص الذاكرة المحيطة غير المستخدَمة إلى النواة مطلقًا.
ما يجب فعله: لا تستخدِم أبدًا ثوابت البايت المضمّنة في البرنامج لإرشادات تحرير الصفحات.
يمكنك زيادة حدود الإصدار ديناميكيًا في وقت التشغيل استنادًا إلى حجم الصفحة الفعلي باستخدام sysconf(_SC_PAGESIZE).
2. ملء الصفحات المستخدَمة من قبل أولاً (التخصيص الكثيف أولاً)
إذا كان المخصّص يوزّع الذاكرة بترتيب الأولوية حسب الأقدمية (FIFO) أو بالتناوب، تتوزّع عمليات التخصيص الجديدة على العديد من الصفحات التي تبلغ سعتها 16 كيلوبايت والمملوءة جزئيًا. يحتفظ عنصر واحد في الصفحة بجميع الـ 16 كيلوبايت في ذاكرة الوصول العشوائي (RAM) الفعلية.
ما يجب فعله: يجب دائمًا تخصيص العناصر الجديدة من الصفحة أو الكتلة الأكثر امتلاءً (الأكثر كثافة) قبل لمس الصفحات الفارغة أو التي تم استخدامها بشكل خفيف. ويؤدي تركيز عمليات التخصيص الجديدة على الصفحات التي تم تعديلها إلى إفراغ الصفحات التي يتم استخدامها بشكل خفيف من العناصر النشطة، وبالتالي يمكن تحرير الصفحة بأكملها التي تبلغ سعتها 16 كيلوبايت لنظام التشغيل.
3- محاذاة مجموعات الذاكرة مع 16 كيلوبايت والحفاظ على أحجام المجموعات معتدلة
يمكن أن تحتوي النطاقات المتعددة الصفحات المصمَّمة لأنظمة 4 كيلوبايت على مساحة ذاكرة غير مستخدَمة مفرطة في أنظمة النواة 16 كيلوبايت. بالإضافة إلى ذلك، فإنّ فئات الأحجام التي لا يمكن تقسيمها بالتساوي إلى 16 كيلوبايت تؤدي إلى حدوث تجزئة في نهاية كل صفحة.
ما يجب فعله:
- تأكَّد من أنّ جميع مجموعات الذاكرة وحدود الألواح وعمليات محاذاة المخزن المؤقت هي مضاعفات دقيقة لحجم الصفحة في وقت التشغيل.
- إعادة تقييم أحجام الامتداد متعدد الصفحات لفئات الكائنات الصغيرة لتجنب تخصيص ألواح كبيرة جدًا تحبس الذاكرة الخاملة.
4. إخلاء الذاكرة الفعلية من المخازن المؤقتة الكبيرة المخزَّنة مؤقتًا على الفور
غالبًا ما تخزّن أدوات التخصيص المخصّصة مؤقتًا مخازن مؤقتة كبيرة (> 64 كيلوبايت) في مجموعة في الذاكرة
ليتمكّن من إعادة استخدامها بدون دفع النفقات العامة لطلبات نظام mmap أو munmap. ومع ذلك، فإنّ الاحتفاظ بهذه المخازن المؤقتة غير الصالحة في الذاكرة يؤدي إلى إهدار ميغابايت من
ذاكرة الوصول العشوائي (RAM) الفعلية أثناء انتظار موقّت الإخلاء.
الإجراءات المقترَحة: احتفِظ بنطاق عناوين الذاكرة الافتراضية محجوزًا لإعادة الاستخدام السريع،
ولكن استدعِ madvise(..., MADV_DONTNEED) أو madvise(..., MADV_FREE) فورًا
عند إعادة المخزن المؤقت إلى ذاكرة التخزين المؤقت. يستعيد نظام التشغيل ذاكرة الوصول العشوائي (RAM) الفعلية على الفور، بينما يظل بإمكان تطبيقك إعادة استخدام العنوان الافتراضي على الفور بدون إعادة تخصيصه.
5. تصفير الذاكرة عند التحرير بدلاً من التخصيص لضغط ZRAM
يستخدم Android مساحة الإبدال المضغوطة (ZRAM) للاحتفاظ بالتطبيقات التي تعمل في الخلفية في الذاكرة. على الأجهزة التي تبلغ سعة ذاكرتها 16 كيلوبايت، إذا كانت الصفحة تتضمّن عنصرًا واحدًا مباشرًا، ستبقى الصفحة بأكملها التي تبلغ سعتها 16 كيلوبايت مقيمة أو سيتم نقلها إلى ZRAM. البيانات غير الصالحة المتبقية (مثل المؤشرات والسلاسل القديمة) في الأجزاء التي تم إخلاؤها سابقًا من تلك الصفحة لا يتم ضغطها بشكل جيد.
إذا كان المخصِّص يمحو الذاكرة (مثلاً لأغراض الأمان أو عمليات التخصيص التي تبدأ من الصفر)، ننصحك بإجراء ذلك عند إلغاء التخصيص (free()) بدلاً من إجرائه عند التخصيص:
- الصفحات المثبَّتة تستهلك مساحة أقل: يتم ضغط الذاكرة غير المستخدَمة في صفحات بحجم 16 كيلوبايت ممتلئة جزئيًا إلى ما يقرب من الصفر في ZRAM، لذا لا تهدر الصفحات المثبَّتة مساحة الإبدال الفعلية.
- الحد الأدنى من الحمل الزائد لذاكرة التخزين المؤقت: عند استدعاء
free()، تكون الذاكرة مخزّنة مؤقتًا في ذاكرة التخزين المؤقت لوحدة المعالجة المركزية، ما يمنع حدوث المزيد من البيانات غير المتوفرة في ذاكرة التخزين المؤقت لاحقًا.
ملخّص الاقتراحات
| المنطقة | الاقتراح | التأثير المتوقّع |
|---|---|---|
| الحدود الدنيا للحذف | يمكنك توسيع نطاق حدود الإصدار ديناميكيًا باستخدام sysconf(_SC_PAGESIZE). |
يمنع منطق الإصدار من حدوث توقّف تام نهائي. |
| ترتيب التخصيص | يجب تخصيص الذاكرة من الصفحة أو الجزء الأكثر كثافة (الممتلئ تقريبًا) أولاً. | يقلّل من حجم الذاكرة المقيمة (RSS) من خلال تجميع العناصر النشطة معًا. |
| تحديد حجم الامتداد | يجب محاذاة فئات الحجم مع مضاعفات 16 كيلوبايت وتجنُّب استخدام كتل كبيرة الحجم. | تزيل هذه الميزة تجزئة الصفحة الأخيرة وتقلّل من المساحة غير المستخدَمة في الذاكرة. |
| ذاكرة التخزين المؤقت | يجب استدعاء madvise(MADV_DONTNEED) فورًا عند تخزين مخازن مؤقتة كبيرة. |
تزيل هذه الميزة تضخّم ذاكرة الوصول العشوائي غير النشطة مع الحفاظ على سرعة إعادة الاستخدام الافتراضية. |
| مساحة التبديل / ZRAM | يتم تفريغ الذاكرة عند free() بدلاً من تفريغها عند التخصيص. |
تحسين نِسب ضغط ZRAM لتقليل تكلفة الصفحات المثبّتة |
التحقّق من توافق حِزم تطوير البرامج (SDK) مع صفحة الذاكرة بحجم 16 كيلوبايت
تتوافق العديد من حِزم تطوير البرامج (SDK) مع أحجام الصفحات البالغة 16 كيلوبايت، خاصةً إذا أنشأتها بنفسك أو حصلت على إصدارات مسبقة الإنشاء حديثة. ومع ذلك، بما أنّ بعض حِزم SDK المجمَّعة مسبقًا أو إصدارات حِزم SDK غير متوافقة مع حجم 16 كيلوبايت، عليك مراجعة الموقع الإلكتروني لكل موفِّر حزمة SDK لتحديد الإصدار الذي يجب استخدامه مع حجم 16 كيلوبايت.
اختبار تطبيقك في بيئة تتضمن صفحات ذاكرة بحجم 16 كيلوبايت
بعد إنشاء تطبيقك مع توفير إمكانية استخدامه على الأجهزة التي تبلغ سعة صفحات الذاكرة فيها 16 كيلوبايت، عليك اختباره في بيئة تتضمن صفحات ذاكرة بحجم 16 كيلوبايت لمعرفة ما إذا كان تطبيقك سيواجه أي تراجع في الأداء. لإجراء هذا، اتبع هذه الخطوات:
إعداد حزمة تطوير البرامج (SDK) لنظام التشغيل Android 15 أو إصدار أحدث
إعداد إحدى بيئات الاختبار التالية:
- إعداد Android Emulator باستخدام صورة نظام تستند إلى 16 كيلوبايت وتعمل بالإصدار Android 15
- استخدام Cuttlefish مع صفحات بحجم 16 كيلوبايت على معالِج ARM64
- محاكاة جهاز Cuttlefish بحجم صفحة 16 كيلوبايت على x86-64
- تفعيل الوضع 16 كيلوبايت على جهاز باستخدام خيارات المطوّرين
- استخدام Samsung Remote Test Lab على الأجهزة المتوافقة مع الصفحات بحجم 16 كيلوبايت
ابدأ تشغيل جهازك الاختباري، ثم نفِّذ الأمر التالي للتأكّد من أنّه يستخدم بيئة تتضمّن صفحات ذاكرة بحجم 16 كيلوبايت:
adb shell getconf PAGE_SIZEمن المفترض أن يعرض الأمر القيمة
16384.نفِّذ الأمر
zipalignالتالي للتأكّد من أنّ تطبيقك متوافق مع حجم 16 كيلوبايت، حيث يشير APK_NAME إلى اسم ملف APK الخاص بتطبيقك:zipalign -c -P 16 -v 4 APK_NAME.apkاختبِر تطبيقك بدقة، مع التركيز على أي مناطق قد تتأثر بتغيير مثيلات الرموز التي تشير إلى أحجام صفحات معيّنة.
إعداد Android Emulator باستخدام صورة نظام تستند إلى 16 كيلوبايت
لإعداد بيئة بحجم 16 كيلوبايت باستخدام Android Emulator، اتّبِع الخطوات التالية:
- في "استوديو Android"، انقر على الأدوات > SDK Manager.
في علامة التبويب منصات حزم تطوير البرامج (SDK)، اختَر عرض تفاصيل الحزمة، ثم وسِّع قسم Android VanillaIceCream أو قسم أحدث واختَر إحدى صورتَي نظام المحاكي التاليتَين أو كلتيهما، حسب الأجهزة الافتراضية التي تريد إنشاءها:
- صورة نظام Google APIs التجريبية بحجم صفحة 16 كيلوبايت لمعالجات ARM 64 v8a
- Google APIs Experimental 16 KB Page Size Intel x86_64 Atom System Image
انقر على تطبيق > حسنًا لتنزيل صور النظام التي اخترتها.
اتّبِع الخطوات لإعداد جهاز افتراضي لنظام التشغيل Android 15، وعندما يُطلب منك اختيار صورة نظام، اختَر صورة النظام بحجم 16 كيلوبايت التي نزّلتها. إذا لم يتم اقتراحها تلقائيًا، يمكنك العثور على صورة النظام بحجم 16 كيلوبايت في علامة التبويب صور أخرى.
تشغيل المحاكي
بعد الانتهاء من إعداد Android Emulator والأجهزة الافتراضية، شغِّل المحاكي من قائمة جهاز الاختبار أو من سطر الأوامر.
تفعيل الوضع 16 كيلوبايت على جهاز باستخدام خيارات المطوّرين
فعِّل خيار المطوّر التشغيل مع صفحات حجمها 16 كيلوبايت لتشغيل الجهاز في وضع 16 كيلوبايت.
في إصدارات QPR من نظام التشغيل Android 15، يمكنك استخدام خيار المطوّرين المتاح على أجهزة معيّنة لإعادة تشغيل الجهاز في وضع 16 كيلوبايت وإجراء اختبار على الجهاز فقط. قبل استخدام خيار المطوّرين، انتقِل إلى الإعدادات > النظام > تحديثات البرامج وطبِّق أي تحديثات متوفّرة.
يتوفّر خيار المطوّرين هذا على الأجهزة التالية:
Pixel 8 وPixel 8 Pro (مع الإصدار 1 من حزمة إصلاح الأخطاء في نظام Android 15 أو إصدار أحدث)
Pixel 8a (مع الإصدار 1 من حزمة إصلاح الأخطاء في نظام Android 15 أو إصدار أحدث)
Pixel 9 و9 Pro و9 Pro XL (مع الإصدار الثاني من حزمة Android 15 QPR أو إصدار أحدث)
Pixel 9a (مع نظام التشغيل Android 16 أو إصدار أحدث)
وضع التوافق مع الإصدارات القديمة بحجم 16 كيلوبايت
تحذير في وضع التوافق مع حجم الصفحة
يتوفّر خيار التوافق مع الإصدارات القديمة بحجم 16 كيلوبايت عندما يعمل الجهاز بنواة متوافقة مع صفحات حجمها 16 كيلوبايت. يشغّل مدير الحِزم تطبيقًا في وضع التوافق مع الإصدارات القديمة بحجم 16 كيلوبايت عند استيفاء الشروط التالية:
- إذا كان التطبيق يحتوي على ملفات ELF (بالامتداد
.so) مع قسم LOAD محاذاته 4 كيلوبايت - إذا كانت حزمة APK المضغوطة تحتوي على ملفات ELF غير مضغوطة بحجم 4 كيلوبايت متوافقة مع ZIP.
إذا فعّل مدير الحِزم وضع التوافق مع الإصدارات السابقة بحجم 16 كيلوبايت لأحد التطبيقات، سيعرض التطبيق تحذيرًا عند تشغيله لأول مرة يفيد بأنّه يعمل في وضع التوافق مع الإصدارات السابقة بحجم 16 كيلوبايت.
يتيح وضع التوافق مع الإصدارات القديمة لصفحات الذاكرة بحجم 16 كيلوبايت لبعض التطبيقات العمل، ولكن للحصول على أفضل موثوقية واستقرار، يجب أن تكون التطبيقات متوافقة مع صفحات الذاكرة بحجم 16 كيلوبايت.
في صفحة معلومات التطبيق، ضمن الإعدادات المتقدّمة، فعِّل الإعداد تشغيل التطبيق في وضع التوافق مع حجم الصفحة أو أوقِفه لتفعيل وضع التوافق مع الإصدارات السابقة الذي يعرض الصفحة بحجم 16 كيلوبايت أو إيقافه لتطبيق معيّن. لا يظهر هذا الإعداد إلا عندما يكون الجهاز يعمل بصفحات حجمها 16 كيلوبايت.
إعداد وضع التوافق مع حجم الصفحة
لفرض التوافق مع الإصدارات القديمة بحجم 16 كيلوبايت لكل تطبيق على الجهاز، اتّبِع الخطوات التالية:
adb shell setprop bionic.linker.16kb.app_compat.enabled true
adb shell setprop pm.16kb.app_compat.disabled false
لإيقاف توافق 16 كيلوبايت مع الإصدارات القديمة لكل تطبيق على الجهاز، اتّبِع الخطوات التالية:
adb shell setprop bionic.linker.16kb.app_compat.enabled false
adb shell setprop pm.16kb.app_compat.disabled true
في Android 17، يمكنك أيضًا فرض إيقاف التوافق مع الإصدارات القديمة بحجم 16 كيلوبايت لكل تطبيق، ما يؤدي إلى إيقاف أي ملف ثنائي غير متوافق على الفور:
adb shell setprop bionic.linker.16kb.app_compat.enabled fatal
adb shell setprop pm.16kb.app_compat.disabled true
اضبط السمة android:pageSizeCompat على "مفعَّلة" أو "غير مفعَّلة" لتفعيل وضع التوافق مع الإصدارات القديمة أو إيقافه لتطبيق معيّن في AndroidManifest.xml. عند ضبط هذه السمة، لن يعرض التطبيق تحذيرات بشأن وضع التوافق مع الإصدارات القديمة عند تشغيله.