أخبار المنتجات

ضبط قواعد الاحتفاظ ببيانات R8 وتحديد المشاكل فيها وحلّها

مدّة القراءة: 7 دقائق
عرض الملف الشخصي لـ "أجيش باي" عرض الملف الشخصي لـ "بِن وايس"
Ajesh Pai & Ben Weiss

في عملية تطوير تطبيقات Android الحديثة، يُعدّ توفير تطبيق صغير وسريع وآمن من التوقعات الأساسية للمستخدمين. الأداة الأساسية التي يستخدمها نظام تصميم Android لتحقيق ذلك هي أداة التحسين R8 ، وهي برنامج التجميع الذي يتعامل مع الرموز غير المستخدَمة وإزالة الموارد لتقليل الحجم، وإعادة تسمية الرموز أو تصغيرها، وتحسين التطبيق.

يُعدّ تفعيل R8 خطوة مهمة في إعداد تطبيق للإصدار، ولكنّه يتطلّب من المطوّرين تقديم إرشادات في شكل "قواعد الاحتفاظ".

بعد قراءة هذه المقالة، شاهِد فيديو "أسبوع تسليط الضوء على الأداء" حول تفعيل أداة التحسين R8 وتصحيح الأخطاء وحلّ المشاكل فيها على YouTube.

 

 

أهمية قواعد الاحتفاظ بالبيانات

تنشأ الحاجة إلى كتابة قواعد Keep Rules من تعارض أساسي: R8 هي أداة تحليل ثابت، ولكن غالبًا ما تعتمد تطبيقات Android على أنماط التنفيذ الديناميكي، مثل الانعكاس أو عمليات الاستدعاء داخل الرمز البرمجي الأصلي وخارجه باستخدام JNI (واجهة Java الأصلية).

تنشئ أداة R8 رسمًا بيانيًا للرمز المستخدَم من خلال تحليل عمليات الاستدعاء المباشر. عند الوصول إلى الرمز بطريقة ديناميكية، لا يمكن للتحليل الثابت في R8 توقّع ذلك، وسيتم تحديد هذا الرمز على أنّه غير مستخدَم وإزالته، ما يؤدي إلى حدوث أعطال أثناء وقت التشغيل.

قاعدة الحفاظ هي تعليمات صريحة لمترجم R8، وتنص على ما يلي: "هذا الصنف أو الطريقة أو الحقل المحدّد هو نقطة دخول سيتم الوصول إليها بشكل ديناميكي في وقت التشغيل. يجب الاحتفاظ بها، حتى إذا لم تتمكّن من العثور على إشارة مباشرة إليها".

يمكنك الاطّلاع على الدليل الرسمي لمزيد من التفاصيل حول قواعد الاحتفاظ.

مكان كتابة "قواعد الاحتفاظ بالبيانات"

تتم كتابة قواعد الاحتفاظ المخصّصة لأحد التطبيقات في ملف نصي. وبموجب الاصطلاح، يُسمّى هذا الملف proguard-rules.pro ويقع في جذر التطبيق أو وحدة المكتبة. يتم بعد ذلك تحديد هذا الملف في نوع التصميم release ضمن ملف build.gradle.kts في الوحدة.

release {

    isShrinkResources = true

    isMinifyEnabled = true

    proguardFiles(

        getDefaultProguardFile("proguard-android-optimize.txt"),

        "proguard-rules.pro",

    )

}

استخدام الملف التلقائي الصحيح

تستورد الطريقة getDefaultProguardFile مجموعة تلقائية من القواعد التي توفّرها حزمة تطوير البرامج (SDK) لنظام التشغيل Android. عند استخدام ملف غير صحيح، قد لا يكون تطبيقك محسَّنًا. احرص على استخدام proguard-android-optimize.txt. يوفّر هذا الملف قواعد الاحتفاظ التلقائية لمكوّنات Android العادية ويتيح تحسينات R8 على الرموز البرمجية. لا يوفّر proguard-android.txt القديم سوى قواعد Keep، ولكنه لا يفعّل تحسينات R8.

progaurd.png

بما أنّ هذه المشكلة تؤثر بشكل كبير في الأداء، سنبدأ بتنبيه المطوّرين بشأن استخدام الملف غير الصحيح، وذلك بدءًا من الإصدار 3 من تحديث Narwhal من "استوديو Android". واعتبارًا من الإصدار 9.0 من المكوّن الإضافي لنظام Gradle المتوافق مع Android، لن نتيح استخدام ملف proguard-android.txt القديم. لذا، احرص على الترقية إلى الإصدار المحسّن.

كيفية كتابة "قواعد الاحتفاظ بالبيانات"

تتألف قاعدة الاحتفاظ من ثلاثة أجزاء رئيسية:

  1. أحد الخيارات مثل -keep أو -keepclassmembers
  2. المعدّلات الاختيارية مثل allowshrinking
  3. مواصفات فئة تحدّد الرمز المطلوب مطابقته

للاطّلاع على البنية الكاملة والأمثلة، يُرجى الرجوع إلى الإرشادات حول إضافة قواعد الاحتفاظ.

أنماط Keep Rule المضادة

من المهم معرفة أفضل الممارسات، ولكن أيضًا معرفة الأنماط المضادة. غالبًا ما تنشأ هذه الأنماط المضادة من سوء الفهم أو اختصارات تحديد المشاكل وحلّها، ويمكن أن تكون كارثية على أداء الإصدار المتاح للجميع.

الخيارات العامّة

هذه العلامات هي مفاتيح تبديل عامة يجب عدم استخدامها مطلقًا في بنية الإصدار. وهي مخصّصة فقط لتصحيح الأخطاء المؤقت بهدف تحديد المشكلة.

يؤدي استخدام -dontotptimize إلى إيقاف تحسينات الأداء التي توفّرها أداة R8، ما يؤدي إلى إبطاء التطبيق.

عند استخدام -dontobfuscate، يتم إيقاف جميع عمليات إعادة التسمية، وعند استخدام -dontshrink، يتم إيقاف إزالة الرموز البرمجية غير النشطة. تؤدي كلتا القاعدتين العالميتين إلى زيادة حجم التطبيق.

تجنَّب استخدام هذه العلامات العامة في بيئة التشغيل الفعلي قدر الإمكان للحصول على تجربة أفضل للمستخدمين.

قواعد الاحتفاظ الواسعة النطاق بشكل مفرط

إنّ أسهل طريقة لإبطال مزايا R8 هي كتابة قواعد Keep واسعة النطاق. توجّه قواعد مثل القاعدة أدناه أداة التحسين R8 إلى عدم تصغير أي فئة في هذه الحزمة أو أي من حِزمها الفرعية، وعدم تشويشها، وعدم تحسينها. يؤدي ذلك إلى إزالة مزايا R8 بالكامل من تلك الحزمة بأكملها. بدلاً من ذلك، حاوِل كتابة "قواعد الاحتفاظ" ضيّقة ومحدّدة.
 

-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMS

عامل النفي (!)

يبدو أنّ عامل النفي (!) هو طريقة فعّالة لاستبعاد حزمة من إحدى القواعد. لكنّ الأمر ليس بهذه البساطة. لِنأخذ المثال التالي:

-keep class !com.example.my_package.** { *; } // USE WITH CAUTION

قد تعتقد أنّ هذه القاعدة تعني "عدم الاحتفاظ بالفئات فيcom.example.package"، ولكنّها تعني في الواقع "الاحتفاظ بكل فئة وطريقة وسمة في التطبيق بأكمله غير موجودة في com.example.package". إذا كان ذلك مفاجئًا لك، من الأفضل التحقّق من أي عمليات نفي في إعدادات R8.

قواعد مكرّرة لمكوّنات Android

هناك خطأ شائع آخر وهو إضافة "قواعد الإبقاء" يدويًا إلى Activities أو Services أو BroadcastReceivers في تطبيقك. هذا غير ضروري. يتضمّن ملف proguard-android-optimize.txt التلقائي القواعد ذات الصلة لكي تعمل هذه المكوّنات العادية في Android بدون أي إعدادات إضافية.

توفّر العديد من المكتبات أيضًا "قواعد الاحتفاظ" الخاصة بها. لذلك، لن تحتاج إلى كتابة قواعدك الخاصة لهذه الحقول. في حال حدوث مشكلة في "قواعد الاحتفاظ" من مكتبة تستخدمها، من الأفضل التواصل مع مؤلف المكتبة لمعرفة سبب المشكلة.

أفضل الممارسات المتعلّقة بقاعدة Keep

بعد أن تعرّفت على ما لا يجب فعله، لنتحدّث الآن عن أفضل الممارسات.

كتابة "قواعد الاحتفاظ" محدّدة

يجب أن تكون "قواعد الاحتفاظ" الجيدة ضيقة ومحددة قدر الإمكان. يجب الاحتفاظ فقط بما هو ضروري، ما يسمح لأداة R8 بتحسين كل ما عدا ذلك.
 

القاعدةالجودة

 

-keep class com.example.** { ; }

 

منخفض: يتم الاحتفاظ بحزمة كاملة وحِزمها الفرعية

 

-keep class com.example.MyClass { ; }

 

منخفض: يحتفظ بفئة كاملة من المرجّح أنّها لا تزال واسعة جدًا
-keepclassmembers class com.example.MyClass {

    private java.lang.String secretMessage;

    public void onNativeEvent(java.lang.String);

}
عالية: يتم الاحتفاظ بالطرق والسمات ذات الصلة فقط من فئة معيّنة

استخدام الأسلاف المشتركين

بدلاً من كتابة "قواعد الاحتفاظ" منفصلة لنماذج بيانات مختلفة متعددة، اكتب قاعدة واحدة تستهدف صنفًا أساسيًا أو واجهة مشتركة. توضّح القاعدة أدناه أنّ على R8 الاحتفاظ بأي عناصر من الفئات التي تنفّذ هذه الواجهة، وهي قابلة للتوسيع بشكل كبير.

# Keep all fields of any class that implements SerializableModel

-keepclassmembers class * implements com.example.models.SerializableModel {

    <fields>;

}

استخدام التعليقات التوضيحية لاستهداف صفوف متعددة

أنشئ تعليقًا توضيحيًا مخصّصًا (مثل @Serialize) واستخدِمه "لوضع علامة" على الفئات التي تحتاج إلى الاحتفاظ بحقولها. هذا نمط آخر واضح وتصريحي وقابل للتوسيع بشكل كبير. يمكنك إنشاء "قواعد Keep" للتعليقات التوضيحية الحالية من الأُطر التي تستخدمها أيضًا.

# Keep all fields of any class annotated with @Serialize

-keepclassmembers class * {

    @com.example.annotations.Serialize <fields>;

}

اختيار "خيار الاحتفاظ" المناسب

يُعدّ خيار "الاحتفاظ" الجزء الأكثر أهمية في القاعدة. وقد يؤدي اختيار الخيار غير الصحيح إلى إيقاف التحسين بدون داعٍ.

خيار Keepوظيفة هذه الميزة
-keepيمنع إزالة الفئة والأعضاء المذكورين في البيان أو إعادة تسميتهم.
-keepclassmembersيمنع الأعضاء المحدّدين من إزالتهم أو إعادة تسميتهم، ولكنّه يسمح بإزالة الصف نفسه فقط في الصفوف التي لم تتم إزالتها بأي طريقة أخرى.
-keepclasseswithmembersمزيج: يتم الاحتفاظ بالفئة وعناصرها فقط إذا كانت جميع العناصر المحدّدة متوفّرة.

يمكنك الاطّلاع على مزيد من المعلومات حول خيار "الاحتفاظ" في مستندات خيارات الاحتفاظ.

السماح بالتحسين باستخدام المعدِّلات

تؤدي المعدِّلات، مثل allowshrinking وallowobfuscation، إلى تخفيف قاعدة -keep واسعة النطاق، ما يمنح R8 القدرة على التحسين مرة أخرى. على سبيل المثال، إذا كانت مكتبة قديمة تفرض عليك استخدام -keep في فئة بأكملها، قد تتمكّن من استعادة بعض التحسينات من خلال السماح بالتصغير والتشويش:

# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it.

-keep,allowshrinking,allowobfuscation class com.example.LegacyClass

إضافة خيارات عامة لتحسين الأداء بشكل إضافي

بالإضافة إلى قواعد الاحتفاظ بالبيانات، يمكنك إضافة علامات عامة إلى ملف إعدادات R8 لتشجيع المزيد من التحسين.

‫-repackageclasses هو خيار فعّال يوجّه R8 إلى نقل جميع الفئات التي تم تشويشها إلى حزمة واحدة. يؤدي ذلك إلى توفير مساحة كبيرة في ملف DEX من خلال إزالة سلاسل أسماء الحِزم المكرّرة.

تسمح -allowaccessmodification لبرنامج R8 بتوسيع نطاق الوصول (على سبيل المثال، من private إلى public) لتفعيل التضمين الأكثر فعالية. يتم تفعيل ذلك تلقائيًا عند استخدام proguard-android-optimize.txt.

تحذير: يجب على مطوّري المكتبات عدم إضافة علامات التحسين العامة هذه إلى قواعد المستهلكين، لأنّه سيتم تطبيقها بشكل إلزامي على التطبيق بأكمله.

ولزيادة الوضوح، سنبدأ في الإصدار 9.0 من "المكوّن الإضافي لنظام Gradle المتوافق مع Android" بتجاهل علامات التحسين العامة من المكتبات تمامًا. 

أفضل الممارسات المتعلّقة بالمكتبات

تعتمد جميع تطبيقات Android على المكتبات بطريقة أو بأخرى. لنتحدّث الآن عن أفضل الممارسات المتعلّقة بالمكتبات.

لمطوّري المكتبات

إذا كانت مكتبتك تستخدم الانعكاس أو JNI، تقع على عاتقك مسؤولية توفير قواعد Keep اللازمة للمستهلكين. يتم وضع هذه القواعد في ملف consumer-rules.pro، ثم يتم تجميعها تلقائيًا داخل ملف AAR الخاص بالمكتبة.

android {

    defaultConfig {

        consumerProguardFiles("consumer-rules.pro")

    }

    ...

}

لمستخدمي المكتبة

فلترة "قواعد الاحتفاظ بالبيانات" التي تتضمّن مشاكل

إذا كان عليك استخدام مكتبة تتضمّن قواعد Keep Rules إشكالية، يمكنك فلترتها في ملف build.gradle.kts بدءًا من الإصدار 9.0 من المكوّن الإضافي لنظام Gradle المتوافق مع Android، ما يطلب من R8 تجاهل القواعد الواردة من تبعية معيّنة.

release {

    optimization.keepRules {

        // Ignore all consumer rules from this specific library

        it.ignoreFrom("com.somelibrary:somelibrary")

    }

}

أفضل قاعدة احتفاظ بالبيانات هي عدم وجود قاعدة احتفاظ بالبيانات

تتمثّل استراتيجية إعدادات R8 المثالية في إزالة الحاجة إلى كتابة قواعد الاحتفاظ بالبيانات تمامًا. يمكن تحقيق ذلك في العديد من التطبيقات من خلال اختيار مكتبات حديثة تعطي الأولوية لإنشاء الرموز على الانعكاس. من خلال إنشاء الرموز البرمجية، يمكن لأداة التحسين تحديد الرموز البرمجية المستخدَمة فعليًا في وقت التشغيل والرموز البرمجية التي يمكن إزالتها بسهولة أكبر. عدم استخدام أي انعكاس ديناميكي يعني أيضًا عدم وجود نقاط دخول "مخفية"، وبالتالي لا حاجة إلى قواعد Keep. عند اختيار مكتبة جديدة، ننصحك دائمًا باختيار حلّ يستخدم إنشاء الرموز البرمجية بدلاً من الانعكاس.

لمزيد من المعلومات حول كيفية اختيار المكتبات، يُرجى الاطّلاع على مقالة اختيار المكتبة بحكمة.

تصحيح أخطاء إعدادات R8 وتحديد المشاكل وحلّها

عندما تزيل أداة R8 رمزًا كان من المفترض الاحتفاظ به، أو إذا كان حجم حِزمة APK أكبر من المتوقع، استخدِم هذه الأدوات لتشخيص المشكلة.

البحث عن قواعد Keep المكرّرة والعامة

بما أنّ R8 يدمج القواعد من عشرات المصادر، قد يصعب معرفة مجموعة القواعد "النهائية". تؤدي إضافة هذا الخيار إلى ملف proguard-rules.pro إلى إنشاء تقرير كامل:

# Outputs the final, merged set of rules to the specified file

-printconfiguration build/outputs/logs/configuration.txt

يمكنك البحث في هذا الملف للعثور على قواعد مكرّرة أو تتبُّع قاعدة تتسبّب في مشاكل (مثل -dontoptimize) والرجوع إلى المكتبة المحدّدة التي تضمّنتها.

Ask R8: Why are you keeping this?

إذا كان صف تتوقّع إزالته لا يزال في تطبيقك، يمكن أن يوضّح لك R8 السبب. ما عليك سوى إضافة هذه القاعدة:

# Asks R8 to explain why it's keeping a specific class

class com.example.MyUnusedClass

-whyareyoukeeping 

أثناء عملية الإنشاء، ستعرض أداة R8 سلسلة المراجع الدقيقة التي أدّت إلى الاحتفاظ بهذه الفئة، ما يتيح لك تتبُّع المرجع وتعديل قواعدك.

للحصول على دليل كامل، يُرجى الاطّلاع على قسم تحديد المشاكل في R8 وحلّها.

الخطوات التالية

‫R8 هي أداة فعّالة لتحسين أداء تطبيقات Android. وتعتمد فعاليته على فهم صحيح لطريقة عمله كمحرّك تحليل ثابت.

من خلال كتابة قواعد محدّدة على مستوى الأعضاء والاستفادة من العناصر الأصلية والتعليقات التوضيحية واختيار خيارات الحفظ المناسبة بعناية، يمكنك الاحتفاظ بما هو ضروري بالضبط. أما الممارسة الأكثر تقدّمًا، فهي إلغاء الحاجة إلى القواعد تمامًا من خلال اختيار مكتبات حديثة مستندة إلى إنشاء الرموز البرمجية بدلاً من المكتبات السابقة المستندة إلى الانعكاس.

أثناء متابعة "أسبوع تسليط الضوء على الأداء"، احرص على مشاهدة فيديو "أسبوع تسليط الضوء" اليوم على YouTube ومواصلة تحدّي R8. استخدِم العلامة ‎ #optimizationEnabled لطرح أي أسئلة حول تفعيل R8 أو تحديد المشاكل وحلّها. نحن في الخدمة.

حان الوقت للاطّلاع على المزايا بنفسك.

ندعوك إلى تفعيل الوضع الكامل لأداة R8 في تطبيقك اليوم.

  1. اتّبِع أدلة المطوّرين للبدء: تفعيل تحسين التطبيق.
  2. تحقَّق مما إذا كنت لا تزال تستخدم proguard-android.txt واستبدِله بـ proguard-android-optimize.txt.
  3. بعد ذلك، قِس التأثير. لا تكتفِ بالشعور بالفرق، بل تأكَّد منه. يمكنك قياس تحسّن الأداء من خلال تعديل الرمز من نموذج تطبيق Macrobenchmark التجريبي على GitHub لقياس أوقات بدء التشغيل قبل وبعد التعديل.

نحن على ثقة بأنّك ستلاحظ تحسّنًا ملحوظًا في أداء تطبيقك.

يمكنك أيضًا استخدام الهاشتاغ #AskAndroid لطرح أسئلتك. يتابع خبراء Google أسئلتك ويجيبون عنها طوال الأسبوع.

ترقَّبوا منشورنا غدًا الذي سنتحدّث فيه عن ميزة "التحسين المستند إلى الملف الشخصي" باستخدام "ملفات المرجع" و"ملفات بدء التشغيل"، وسنشارككم معلومات حول التحسينات التي أجريناها على أداء العرض في Compose خلال الإصدارات السابقة، بالإضافة إلى اعتبارات الأداء المتعلقة بالعمل في الخلفية.

من تأليف:
متابعة القراءة