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

ضبط قواعد الاحتفاظ ببيانات 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 من تحديثات استوديو Android Narwhal. واعتبارًا من الإصدار 9.0 من المكوّن الإضافي لنظام Gradle المتوافق مع Android، لن نتيح استخدام ملف proguard-android.txt القديم. لذا، احرص على الترقية إلى الإصدار المحسّن.

كيفية كتابة قواعد Keep

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

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

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

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

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

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

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

يؤدي استخدام -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

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

كتابة "قواعد 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);

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

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

بدلاً من كتابة قواعد Keep منفصلة لنماذج بيانات مختلفة متعددة، اكتب قاعدة واحدة تستهدف صنفًا أساسيًا أو واجهة مشتركة. تطلب القاعدة أدناه من 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" التي تتضمّن مشاكل

إذا كان عليك استخدام مكتبة تتضمّن قواعد Keep Rules إشكالية، يمكنك فلترتها في ملف build.gradle.kts بدءًا من الإصدار 9.0 من AGP، ما يطلب من 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 خلال الإصدارات السابقة، بالإضافة إلى اعتبارات الأداء المتعلقة بالعمل في الخلفية.

المؤلف:
متابعة القراءة