في السابق، كان نظام التشغيل 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++. إذا كان تطبيقك يستخدم Android NDK، يعني ذلك أنّه يستخدم رموزًا برمجية أصلية.
- يرتبط تطبيقك بأي مكتبات مجمّعة من رموز برمجية أصلية أو الاعتماديات التابعة لجهات خارجية (مثل حِزم تطوير البرامج) التي تستخدمها.
- تم إنشاء تطبيقك باستخدام أداة إنشاء تطبيقات خارجية تستخدم مكتبات مجمّعة من رموز برمجية أصلية على الجهاز.
تحديد المكتبات المجمّعة من رموز برمجية أصلية باستخدام "أداة تحليل ملفات APK"
أداة تحليل ملفات APK هي أداة تتيح لك تقييم الجوانب المختلفة لملف APK الذي تم إنشاؤه. للتحقّق مما إذا كان تطبيقك يستخدم رموزًا برمجية أصلية (بغض النظر عما إذا كان متوافقًا مع حجم 16 كيلوبايت):
- افتح استوديو Android، ثم انقر على ملف > فتح واختَر أي مشروع.
من شريط القوائم، انقر على Build (إنشاء) > Analyze APK... (تحليل حزمة APK...)
اختَر حزمة APK التي تريد تحليلها.
ابحث في المجلد
libالذي يستضيف ملفات العناصر المشتركة (.so) في حال توفّرها. إذا كان هناك أي ملفات كائنات مشتركة، فإن تطبيقك يستخدم الرموز البرمجية الأصلية. يعرض عمود المحاذاة رسائل تحذيرية لأي ملفات بها مشاكل في المحاذاة. إذا لم تكن هناك ملفات عناصر مشتركة أو لم يكن هناك مجلدlib، يعني ذلك أنّ تطبيقك لا يستخدم رموزًا برمجية أصلية.
رصد مشاكل التوافق من خلال عمليات التحقّق الآلية
يُرسل إليك "استوديو Android" تحذيرات استباقية إذا كانت المكتبات أو حِزم APK المُنشأة مسبقًا غير متوافقة مع حجم الصفحة الذي يبلغ 16 كيلوبايت. استخدِم أداة تحليل ملفات APK لمراجعة المكتبات التي تحتاج إلى تحديث أو لمعرفة ما إذا كانت هناك حاجة إلى إجراء أي تغييرات على الرمز البرمجي.
أداة Lint في "استوديو Android" تبرز أيضًا المكتبات المجمّعة من رموز برمجية أصلية التي لم تتم محاذاتها مع صفحة الذاكرة بحجم 16 كيلوبايت.
التحقّق من محاذاة أجزاء ELF للمكتبات المشتركة
بالنسبة إلى أي مكتبات مشتركة، تأكَّد من أنّ أجزاء ELF الخاصة بالمكتبات المشتركة تتم محاذاتها بشكل صحيح باستخدام محاذاة ELF بحجم 16 كيلوبايت.
استخدام "أداة تحليل ملفات APK" (يُنصح به)
أسهل طريقة للتحقّق من محاذاة المكتبات المشتركة هي استخدام أداة تحليل ملفات APK في "استوديو Android". إذا لم يكن ملف الكائن المشترك متوافقًا مع حجم 16 كيلوبايت، سيعرض عمود التوافق رسالة تحذير.
استخدام النص البرمجي check_elf_alignment.sh (على نظام التشغيل Linux أو macOS)
إذا كنت تطوّر على نظام التشغيل Linux أو macOS وتفضّل استخدام نص برمجي، يمكنك استخدام النص البرمجي 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) لجعل أقسام هدف إعادة التوطين (مثل .got و.data.rel.ro) لملف الكائن المشترك للقراءة فقط بعد اكتمال عمليات إعادة التوطين الديناميكية.
فعِّل علامة RELRO في إصدارك.
عندما يشارك GNU_RELRO قسم LOAD قابلاً للكتابة مع بيانات قابلة للكتابة لاحقة
(مثل .data أو .bss)، يجب أن يكون عنوان نهاية قسم GNU_RELRO
(VirtAddr + MemSiz) متوافقًا مع حجم 16 كيلوبايت (0x4000). وإلا، عندما
يضع رابط Android علامة على صفحة بحجم 16 كيلوبايت تحتوي على نهاية
GNU_RELRO كصفحة للقراءة فقط، تصبح أي بيانات قابلة للكتابة مجاورة موجودة في الجزء المتبقي
من تلك الصفحة نفسها بحجم 16 كيلوبايت للقراءة فقط أيضًا، ما يؤدي إلى تعطُّل التطبيق في
وقت التشغيل بسبب خطأ في التقسيم (SIGSEGV) عند كتابة تلك البيانات.
يحدث هذا عادةً إذا تم إنشاء ملف .so باستخدام سلسلة أدوات NDK r27 والإصدارات الأقدم بدون تفعيل العلامات ذات الصلة.
استخدام "أداة تحليل ملفات APK" (يُنصح به)
يمكنك استخدام أداة تحليل ملفات APK في استوديو Android للتحقّق من حالة محاذاة RELRO لملفات العناصر المشتركة مباشرةً.
استخدام أدوات سطر الأوامر
إذا كنت تفضّل التحقّق من المحاذاة يدويًا أو كنت بحاجة إلى برمجة العملية، اتّبِع الخطوات التالية:
الخطوة 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 مع حجم 16 كيلوبايت
يكون ملف الكائن المشترك متوافقًا مع الصفحة بحجم 16 كيلوبايت في ما يتعلّق بـ RELRO في حال استيفاء أي من الشروط التالية:
- لا يتوفّر الجزء
GNU_RELROفي عناوين البرنامج. - عنوان نهاية
GNU_RELROمتوافق مع حجم 16 كيلوبايت: مجموع عنوانه الافتراضي (VirtAddr) وحجم الذاكرة (MemSiz) مقسومًا على 16 كيلوبايت (0x4000) يترك باقيًا (باقي القسمة) يساوي صفرًا:(VirtAddr + MemSiz) % 0x4000 == 0. - لا تتبع أي بيانات قابلة للكتابة
GNU_RELROفي شريحةLOADنفسها: يشغلGNU_RELROالجزء المتبقي بالكامل من شريحةLOADالتي تحتوي عليه (أيLOAD.VirtAddr + LOAD.MemSiz == RELRO.VirtAddr + RELRO.MemSiz)، لذا لا تشارك أي أقسام.dataأو.bssقابلة للكتابة الصفحة اللاحقة.
أمثلة
توضّح الأمثلة التالية كيفية تقييم مخرجات llvm-readelf لإعدادات GNU_RELRO المختلفة.
المثال 1: ملف .so غير متوافق (غير متوافق)
In this example, GNU_RELRO shares a RW LOAD segment with subsequent
writable data, and its end address isn't a multiple of 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 هذا غير
متوافق مع حجم 16 كيلوبايت. على جهاز يعرض الصفحة بحجم 16 كيلوبايت، يقرّب الرابط نطاق حماية RELRO إلى حدود الصفحة التي تبلغ 16 كيلوبايت، ويضع علامة على 0xDC000 (فاصل الصفحة السابق) حتى 0xE4000 كصفحات للقراءة فقط. ومع ذلك، يتوفّر قسمَا .data و.bss القابلان للكتابة في ملف .so في النطاق الفرعي من 0xE1000 إلى 0xE4000، ما يؤدي إلى حدوث خطأ في التقسيم عند تعديله في وقت التشغيل. يُرجى إعادة إنشاء ملف .so
كما هو موضّح في قسم
تجميع تطبيقك باستخدام محاذاة ELF بحجم 16 كيلوبايت.
المثال 2: ملف .so متوافق مع قسم RELRO (متوافق)
في هذا المثال، تقع نهاية الجزء GNU_RELRO على حدود صفحة بحجم 16 كيلوبايت:
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 عند حدّ 16 كيلوبايت، ولكنّه لا يزال متوافقًا لأنّه لا توجد بيانات قابلة للكتابة بعده في شريحة 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 التي تحتوي عليها (إدخال LOAD الثاني)، يتبيّن أنّ كليهما يبدأ عند VirtAddr 0x4680 وينتهي عند العنوان نفسه (0x4680 + 0x0980 = 0x5000).
بما أنّ قسم LOAD بالكامل يحتوي على بيانات RELRO فقط، لا تتوفّر
بيانات .data أو .bss قابلة للكتابة في النطاق الفرعي من 0x5000 إلى 0x8000
(إمّا أنّ المكتبة لا تحتوي على بيانات قابلة للكتابة غير RELRO، أو أنّ أي قسم 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 من AGP أو إصدار أحدث
تتطلّب الأجهزة التي تستخدم صفحات ذاكرة بحجم 16 كيلوبايت أن تتم محاذاة التطبيقات التي تتضمّن مكتبات مشتركة غير مضغوطة على حدود متوافقة مع تنسيق zip بحجم 16 كيلوبايت. لإجراء ذلك، عليك الترقية إلى الإصدار 8.5.1 أو إصدار أحدث من "المكوّن الإضافي لنظام Gradle المتوافق مع Android". يُرجى الرجوع إلى قسم مساعد ترقية المكوّن الإضافي لنظام 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 من "المكوّن الإضافي لنظام 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 الذي تستخدمه.
الإصدار r28 من Android NDK والإصدارات الأحدث
يتم تجميع الإصدار r28 من NDK والإصدارات الأحدث بمحاذاة 16 كيلوبايت تلقائيًا.
الإصدار r27 من Android NDK والإصدارات الأقدم
لإتاحة تجميع المكتبات المشتركة التي تمت محاذاتها بحجم 16 كيلوبايت باستخدام الإصدار r27 من 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 كيلوبايت، إذا كانت الصفحة تحتوي على عنصر واحد نشط، تظل الصفحة بأكملها مُخزَّنة في الذاكرة أو يتم نقلها إلى ZRAM. البيانات غير الصالحة المتبقية (مثل المؤشرات والسلاسل القديمة) في الأجزاء التي تم إخلاؤها سابقًا من تلك الصفحة لا يتم ضغطها بشكل جيد.
إذا كان المخصِّص يضبط الذاكرة على الصفر (مثل الأمان أو عمليات التخصيص التي يتم ضبطها على الصفر)، ننصحك بضبطها على الصفر عند إلغاء التخصيص (free()) بدلاً من ضبطها عند التخصيص:
- الصفحات المثبَّتة تستهلك مساحة أقل: يتم ضغط الذاكرة غير المستخدَمة في الصفحات الممتلئة جزئيًا بحجم 16 كيلوبايت إلى ما يقرب من الصفر في ZRAM، لذا لا تستهلك الصفحات المثبَّتة مساحة إبدال فعلية.
- الحدّ الأدنى من عبء ذاكرة التخزين المؤقت: في وقت استدعاء
free()، تكون الذاكرة ساخنة في ذاكرة التخزين المؤقت لوحدة المعالجة المركزية، ما يؤدي إلى تجنُّب البيانات غير المتوفرة في ذاكرة التخزين المؤقت لاحقًا.
ملخّص الاقتراحات
| المنطقة | الاقتراح | التأثير المتوقّع |
|---|---|---|
| الحدود الدنيا للحذف | يمكنك توسيع نطاق حدود الإصدار ديناميكيًا باستخدام sysconf(_SC_PAGESIZE). |
يمنع هذا الإعداد منطق الإصدار من التوقف نهائيًا. |
| ترتيب التخصيص | يتم التخصيص من الصفحة أو الكتلة الأكثر كثافة (الممتلئة تقريبًا) أولاً. | يقلّل من حجم الذاكرة المقيمة (RSS) من خلال تجميع العناصر النشطة معًا. |
| تحديد حجم الامتداد | يجب محاذاة فئات الحجم مع مضاعفات 16 كيلوبايت وتجنُّب الأجزاء الكبيرة الحجم. | تزيل هذه الميزة تجزئة الصفحة الأخيرة وتقلّل من المساحة غير المستخدَمة في الذاكرة. |
| ذاكرة التخزين المؤقت | يجب استدعاء madvise(MADV_DONTNEED) فورًا عند تخزين مخازن مؤقتة كبيرة. |
تمنع هذه الميزة تضخُّم ذاكرة الوصول العشوائي (RAM) غير المستخدَمة مع الحفاظ على سرعة إعادة الاستخدام الافتراضية. |
| مساحة التبديل / ZRAM | يتم تفريغ الذاكرة عند free() بدلاً من تفريغها عند التخصيص. |
تحسين نِسب ضغط ZRAM لتقليل تكلفة الصفحات المثبّتة |
التحقّق من توافق حِزم تطوير البرامج (SDK) مع صفحة الذاكرة بحجم 16 كيلوبايت
تتوافق العديد من حِزم تطوير البرامج (SDK) مع صفحات الذاكرة بحجم 16 كيلوبايت، خاصةً إذا كنت تنشئها بنفسك أو تحصل على إصدارات حديثة مُعدّة مسبقًا. ومع ذلك، بما أنّ بعض حزم SDK المُنشأة مسبقًا أو إصدارات حزم SDK غير متوافقة مع حجم 16 كيلوبايت، عليك مراجعة الموقع الإلكتروني لكل موفّر حزمة SDK لتحديد الإصدار الذي يجب استخدامه مع حجم 16 كيلوبايت.
اختبار تطبيقك في بيئة تتضمن صفحات ذاكرة بحجم 16 كيلوبايت
بعد إنشاء تطبيقك ليتوافق مع الأجهزة التي تستخدم صفحات الذاكرة بحجم 16 كيلوبايت، عليك اختبار تطبيقك في بيئة بحجم 16 كيلوبايت لمعرفة ما إذا كان تطبيقك يواجه أي تراجعات في الأداء. لإجراء هذا، اتبع هذه الخطوات:
إعداد حزمة تطوير البرامج (SDK) لنظام التشغيل Android 15 أو إصدار أحدث
إعداد إحدى بيئات الاختبار التالية:
- إعداد Android Emulator باستخدام صورة نظام Android 15 تستند إلى 16 كيلوبايت
- استخدام 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"، انقر على الأدوات (Tools) > SDK Manager.
في علامة التبويب منصات SDK، اختَر عرض تفاصيل الحزمة، ثم وسِّع قسم Android VanillaIceCream أو قسم أحدث واختَر إحدى صورتَي نظام المحاكي التاليتَين أو كلتيهما، حسب الأجهزة الافتراضية التي تريد إنشاءها:
- صورة نظام Google APIs التجريبية بحجم صفحة 16 كيلوبايت لبنية ARM 64 v8a
- صورة نظام Google APIs التجريبية بحجم صفحة 16 كيلوبايت Intel x86_64 Atom
انقر على تطبيق > حسنًا لتنزيل صور النظام التي اخترتها.
اتّبِع الخطوات لإعداد جهاز افتراضي لنظام التشغيل 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 غير مضغوطة ومتوافقة مع تنسيق ZIP بحجم 4 كيلوبايت
إذا فعّل مدير الحِزم وضع التوافق مع الإصدارات السابقة بحجم 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. عند ضبط هذه السمة، لن يعرض التطبيق تحذيرات وضع التوافق مع الإصدارات القديمة عند تشغيله.