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

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

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

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

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

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

 

 

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

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

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

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

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

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

تتم كتابة قواعد الاحتفاظ المخصّصة لتطبيق معيّن في ملف نصي. وفقًا للاصطلاح، يُطلق على هذا الملف اسم 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 سوى قواعد الاحتفاظ بالبيانات، ولكنه لا يفعّل تحسينات R8.

progaurd.png

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

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

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

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

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

الأنماط المضادة لقواعد Keep

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

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

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

يؤدي استخدام -dontotptimize إلى إيقاف تحسينات أداء R8 بشكل فعّال، ما يؤدي إلى إبطاء التطبيق.

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

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

قواعد الاحتفاظ بالبيانات الواسعة النطاق

أسهل طريقة لإلغاء مزايا R8 هي كتابة قواعد احتفاظ واسعة النطاق. توجّه قواعد الاحتفاظ، مثل القاعدة أدناه، أداة تحسين 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 all fields of any class annotated with @Serialize

-keepclassmembers class * {

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

}

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

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

خيار الاحتفاظما يفعله
-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، تقع على عاتقك مسؤولية توفير قواعد الاحتفاظ اللازمة لمستخدميها. يتم وضع هذه القواعد في ملف consumer-rules.pro، ثم يتم تجميعها تلقائيًا داخل ملف AAR الخاص بالمكتبة.

android {

    defaultConfig {

        consumerProguardFiles("consumer-rules.pro")

    }

    ...

}

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

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

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

release {

    optimization.keepRules {

        // Ignore all consumer rules from this specific library

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

    }

}

أفضل قاعدة احتفاظ هي عدم استخدام أي قاعدة احتفاظ

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

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

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

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

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

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

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

-printconfiguration build/outputs/logs/configuration.txt

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

اسأل R8: لماذا تحتفظ بهذا؟

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

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

class com.example.MyUnusedClass

-whyareyoukeeping 

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

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

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

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

By writing specific, member-level rules, leveraging ancestors and annotations, and carefully choosing the right keep options, you can preserve exactly what is necessary. تتمثّل الممارسة الأكثر تقدّمًا في إلغاء الحاجة إلى القواعد تمامًا من خلال اختيار المكتبات الحديثة المستندة إلى إنشاء الرموز البرمجية بدلاً من المكتبات السابقة المستندة إلى الانعكاس.

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

حان الوقت لتجربة المزايا بنفسك.

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

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

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

يمكنك استخدام علامة التصنيف ‎#AskAndroid على وسائل التواصل الاجتماعي لطرح أسئلتك. يتابع خبراؤنا أسئلتكم ويجيبون عنها طوال الأسبوع.

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

من كتابة:
متابعة القراءة