تحسين رموز النظام الأساسي وموارده باستخدام R8

يُشغّل نظام تصميم Android الأساسي (Soong) برنامج التجميع R8 على الأهداف التي تجمع رمز بايت DEX، بما في ذلك تطبيقات Android (android_app) والاختبارات (android_test) ومكتبات Java القابلة للتثبيت باستخدام compile_dex: true (مثل services.jar)، لتقليص الرموز والموارد غير المستخدَمة وتحسينها وإزالتها. في المكتبات الثابتة (android_library وjava_library الثابتة)، لا يشغّل Soong أداة R8 مباشرةً، بل يستخدم مجموعة سمات optimize لإرفاق قواعد الاحتفاظ بالمستهلك (export_proguard_flags_files: true) ونشرها في أهداف DEX اللاحقة التي تربطها بشكل ثابت.

بالنسبة إلى مهندسي المنصات الذين ينشئون صور النظام أو حزم المورّدين، فإنّ التخلص من قواعد الاحتفاظ الواسعة النطاق يقدّم فوائد مباشرة لسلامة النظام:

  • تقليل حجم قسم النظام: تزيل أداة R8 الفئات والأساليب والموارد غير المستخدَمة قبل تجميع حِزم APK وJAR في /system و/system_ext و/product و/vendor.
  • أدوات تجميع أصغر حجمًا: يعني استخدام عدد أقل من طرق DEX إنشاء ملفات .odex و.vdex أصغر حجمًا بواسطة dex2oat أثناء وقت الإنشاء أو التجميع على الجهاز فقط.
  • تقليل الضغط على ذاكرة وقت التشغيل: بما أنّ نظام التشغيل Android يربط ملفات .odex و.vdex وجداول موارد حِزم APK بذاكرة العمليات باستخدام طلبات mmap التي يتم تقسيمها إلى صفحات عند الطلب، فإنّ الملفات الثنائية الأصغر حجمًا تقلّل من أخطاء الصفحة الرئيسية أثناء بدء تشغيل التطبيق وتقلّل من الرمز المقيم وذاكرة الموارد في جميع العمليات. لمزيد من المعلومات حول تأثير رمز التطبيق المجمَّع في ذاكرة الجهاز، يُرجى الاطّلاع على رمز التطبيق هو الذاكرة.
  • تحسين البرنامج بالكامل بشكل أكثر فعالية: تتيح قيود الاحتفاظ المحدودة لأداة R8 تضمين الطرق، وإلغاء محاكاة الواجهة والمكالمات الافتراضية، وإزالة الحقول غير المستخدَمة، ونشر الثوابت عبر الفئات.

ضبط تحسين R8 في Soong

في ملفات Android.bp، يمكنك ضبط تحسين R8 باستخدام مجموعة سمات optimize في أهداف android_app وjava_library القابلة للتثبيت (أو في وحدات android_library وjava_library الثابتة لتصدير قواعد الاحتفاظ بالبيانات للمستهلكين):

android_app {
    name: "MySystemApp",
    srcs: ["src/**/*.java"],
    optimize: {
        obfuscate: true,
        shrink_resources: true,
    },
}

يلخّص الجدول التالي سمات optimize الأكثر شيوعًا في Soong (المحدّدة في build/soong/java/dex.go):

الخاصية الوصف
enabled يتحكّم هذا الخيار في ما إذا كان سيتم تشغيل R8 على الهدف. القيمة التلقائية هي true لجميع استهدافات android_app.
shrink يتحكّم في عملية "إزالة الرموز غير المستخدَمة" لإزالة الفئات والحقول والطُرق التي يتعذّر الوصول إليها. يتم ضبط القيمة تلقائيًا على true لأهداف android_app (false لوحدات java_library المستقلة ووحدات الاختبار التي تمرر -dontshrink ما لم يتم ضبطها بشكل صريح).
optimize تتحكّم هذه السمة في تحسينات الرموز الثنائية، مثل تضمين الطرق ودمج الفئات ونشر الثوابت وإزالة الفروع غير المستخدَمة. القيمة التلقائية هي true لأهداف android_app (يتم التحكّم فيها من خلال علامة بنية الإصدار RELEASE_R8_OPTIMIZE_BY_DEFAULT).
obfuscate تتحكّم هذه السياسة في إزالة البيانات غير الضرورية من المعرّفات وإعادة تسميتها. يتم ضبطه تلقائيًا على false للتوافق مع الإصدارات السابقة، ولكن يجب ضبطه على true كلما أمكن ذلك لتقليل حجم ملف DEX وإتاحة تحسينات أكثر عمقًا (يُرجى الاطّلاع على تفعيل التشويش كلما أمكن ذلك).
shrink_resources تتم إزالة الموارد غير المستخدَمة (res/ إدخال) من حزمة APK بعد تخفيض حجم الرموز. القيمة التلقائية هي false.
optimized_shrink_resources يتم تشغيل مسار R8 المدمج لتقليص حجم الرموز والموارد، ما يتيح لـ R8 تتبُّع مراجع الرموز والموارد معًا في خطوة واحدة. عند ضبط shrink_resources: true، يتم ضبط القيمة تلقائيًا على RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (true في إصدارات النظام الأساسي العادية).
proguard_flags_files تعرض هذه السمة ملفات .flags أو .pro الخاصة بالوحدة والتي تحتوي على قواعد مخصّصة للاحتفاظ بالبيانات. تجنَّب إضافة ملفات مخصّصة عندما تكون التعليقات التوضيحية العادية أو الإعدادات التلقائية كافية.
export_proguard_flags_files تنشر هذه السمة proguard_flags_files لهذه المكتبة إلى الوحدات اللاحقة التي تعتمد عليها بشكل ثابت.
trace_references_from تعرض هذه السمة قائمة بالأهداف التابعة المباشرة التي يتم تجميعها باستخدام DEX (والمرتبطة من خلال instrumentation_for أو libs) والتي يتتبّعها R8 تلقائيًا ويحتفظ بها (يتم نشرها أيضًا على مستوى صيغ override_android_app وoverride_android_test، وتتوفّر لمكتبات Java المصاحبة غير التابعة عند إقرانها بتقسيم .impl).
allow_traced_references يسمح هذا الحقل للأهداف التابعة التي يتم تجميعها مباشرةً من خلال DEX (المتصلة من خلال instrumentation_for أو libs، مثل الاختبارات على مستوى المستودعات أو اختبارات المورّدين التي لا يمكن إدراجها في trace_references_from) بالموافقة على تتبُّع مراجع الرموز الثنائية في هذا الهدف من خلال ضبط trace_references: true.
trace_references عند ضبطها على true في وحدة تابعة مباشرةً لتجميع DEX لهدف معيّن باستخدام allow_traced_references: true، يتم تشغيل R8 tracereferences على هذه الوحدة وتمرير قواعد الاحتفاظ التي تم إنشاؤها إلى الوحدة المستهدَفة.
protect_api_surface يتم تمرير --protect-api-surface إلى R8. عند ضبطه على java_sdk_library، يتم أيضًا إنشاء قواعد الاحتفاظ بواجهة برمجة التطبيقات Metalava وإرفاقها تلقائيًا في جميع نطاقات واجهة برمجة التطبيقات المفعَّلة (العامة والنظام وmodule-lib وsystem-server والاختبار) بمكتبة التنفيذ.

يمكن أيضًا للمكتبات الثابتة (android_library وjava_library وعمليات استيراد AAR) ضبط required_downstream_optimization: { enabled: true, optimize: true } في Android.bp لطلب أي هدف تجميع DEX لاحق يربطها بشكل ثابت (مباشر أو انتقالي) لتفعيل تقليص R8 وتحسين الرمز الثانوي. يُفعِّل Soong هذا الإعداد تلقائيًا على androidx.compose.runtime_runtime بحيث يتم تحسين الأهداف التي تربط Jetpack Compose باستخدام R8.

تفعيل ميزة "إخفاء مفاتيح فك التشفير" حيثما أمكن

في Soong، يتم ضبط obfuscate تلقائيًا على false للتوافق مع الإصدارات السابقة، ولكن عليك ضبط obfuscate: true بشكل صريح على android_app وأهداف DEX المستقلة حيثما أمكن:

  • تقليل حجم ملفات DEX و.vdex: تؤدي إعادة تسمية الحِزم والفئات والحقول والطرق إلى معرّفات قصيرة (a وb) إلى تقليص حجم مجموعة سلاسل DEX (string_ids وstring_data_item) وموصّفات الأنواع. بما أنّ نظام Android يعيّن ملفات .vdex و.odex في ذاكرة العمليات، فإنّ جداول الرموز الأصغر حجمًا تقلّل بشكل مباشر من حجم قسم النظام واستهلاك الذاكرة أثناء التشغيل.
  • تحسينات أكثر شمولاً للبرنامج بأكمله: يتيح السماح لـ R8 بإعادة تسمية المعرّفات دمج الفئات، وتسوية الحِزم، وإزالة تكرار الفئات الاصطناعية عبر الحِزم التي يجب على R8 تخطّيها لتجنُّب تضارب الأسماء.
  • تحويل تتبع تسلسل استدعاء الدوال البرمجية الكامل إلى رموز: لا يؤدي تفعيل التشويش في Soong إلى تقليل إمكانية تصحيح الأخطاء. بالنسبة إلى كل هدف تم تجميعه باستخدام R8، يُصدر Soong ملف ربط proguard_dictionary في الدليل الوسيط للوحدة، ويجمع كل قواميس الوحدات في عنصر proguard-dict.zip الخاص بعملية الإنشاء، ويُضمِّن تجزئة ربط (--map-id-template) في عنوان DEX، ويُعيد كتابة سمات SourceFile للفئة (--source-file-template) حتى يتمكّن retrace من ترميز تتبُّع تسلسل استدعاء الدوال البرمجية تلقائيًا.

الاحتفاظ بـ obfuscate: false (أو الاحتفاظ بأسماء واجهات برمجة التطبيقات العامة بشكل صريح) فقط من أجل:

  • المكتبات المشتركة في bootclasspath أو system_server classpath: إنّ الوحدات (java_library أو java_sdk_library) التي تعرض واجهة برمجة تطبيقات خارجية مرتبطة ديناميكيًا بوحدات أخرى في وقت التشغيل (مثل أهداف framework.jar أو services.jar أو <uses-library>) يجب أن تحتفظ بـ obfuscate: false أو تحتفظ صراحةً بواجهة برمجة التطبيقات الخاصة بها باستخدام قواعد protect_api_surface: true أو @KeepForApi أو -keep حتى يتمكّن المتصلون الذين تم تجميعهم وفقًا لرموزهم البرمجية الناقصة من حلّ أسماء الفئات والأعضاء في وقت التشغيل. في وحدات java_sdk_library، يؤدي ضبط protect_api_surface: true إلى إنشاء قواعد Metalava ProGuard keep وإرفاقها تلقائيًا في جميع نطاقات واجهة برمجة التطبيقات المفعَّلة (العامة والنظام وmodule-lib وsystem-server والاختبار) بمكتبة التنفيذ. عند تفعيل تحسين R8 العام على أهداف bootclasspath غير القابلة للتحديث (framework-minus-apex وext وframework-location)، فإنّها تتضمّن أيضًا framework-hiddenapi-proguard-rules (الذي تم إنشاؤه بواسطة hiddenapi_to_proguard) للاحتفاظ بالأعضاء المدرَجين في ملفات boot/hiddenapi/*.txt النشطة (مع حذف hiddenapi-max-target-o.txt).

قبل تفعيل obfuscate: true في أحد التطبيقات، تأكَّد من استيفاء المتطلبات الأساسية التالية لعملية النقل:

  • تبعيّات حِزم APK للاختبار الخارجي: إذا كانت حزمة APK الخارجية android_test تضبط instrumentation_for: "MyApp" وتستدعي مباشرةً الفئات أو الطرق الداخلية لـ MyApp، فإنّ إعادة تسمية هذه الرموز تؤدي إلى حدوث NoSuchMethodError أو NoClassDefFoundError في وقت تشغيل الاختبار. اضبط trace_references_from (أو allow_traced_references مع trace_references: true)، ونظِّم الاختبار كحزمة APK ذاتية القياس ترتبط بـ MyApp.impl بشكل ثابت، أو أضِف تعليقات توضيحية إلى عناصر الربط الخاصة بالاختبار باستخدام @VisibleForTesting (راجِع الخطوة 2: نقل قواعد الاحتفاظ المستندة إلى الاختبار) بحيث يتم الاحتفاظ بالرموز الداخلية التي تحتاجها الاختبارات أثناء تمويه بقية التطبيق.
  • الانعكاس المستند إلى السلسلة وJNI: إذا كانت الوحدة تبحث عن الفئات أو الطرق أو الحقول حسب أسماء السلاسل الحرفية (Class.forName أو getDeclaredMethod أو JNI FindClass وGetMethodID) بدون قواعد الاحتفاظ أو التعليقات التوضيحية، يُعيد التشويش تسمية هذه الأهداف ويوقف عمليات البحث في وقت التشغيل. (يُرجى العِلم أنّ الانعكاس غير المُعلَّق يفشل أيضًا في shrink: true وoptimize: true). يمكنك إضافة تعليقات توضيحية إلى نقاط الدخول هذه باستخدام دليل الاحتفاظ بالتعليقات التوضيحية (@UsesReflection أو @UsedByReflection أو @UsedByNative) لكي يحتفظ R8 بأسمائها ويخفي بقية الوحدة.

اتّباع مبدأ عدم استخدام علامات مخصّصة

لا تحتوي وحدة نظام Android الأساسي المثالية على ملفات proguard.flags أو keep.xml مخصّصة. في إصدار نظام Android الأساسي، تكون الغالبية العظمى من قواعد الاحتفاظ المخصّصة زائدة عن الحاجة:

  • الأساسيات في المنصة وأداة AAPT2: يتم الاحتفاظ بمعظم نقاط الدخول تلقائيًا من خلال الأساسيات العامة في المنصة (مثل @Keep وطرق JNI native و@VisibleForTesting) أو يتم إنشاؤها بواسطة أداة AAPT2 من AndroidManifest.xml وموارد التصميم (يُرجى الاطّلاع على فهم قواعد الاحتفاظ التلقائية في المنصة).
  • البدائل المستهدَفة: في حال وجوب الاحتفاظ بنقاط الدخول، يُفضَّل استخدام التعليقات التوضيحية لموقع التعريف (keepanno و@VisibleForTesting) أو قواعد المكتبة التي تم تصديرها (export_proguard_flags_files: true) أو الربط الثابت للاختبار على ملفات .flags المنفصلة (يُرجى الاطّلاع على مراجعة قواعد الاحتفاظ الحالية ونقلها).

قبل إضافة ملف proguard.flags أو keep.xml مخصّص أو الاحتفاظ به، تأكَّد مما إذا كانت القاعدة تتم معالجتها من خلال المراجع الأساسية التلقائية أو يمكن نقلها إلى تعليقات توضيحية للرموز البرمجية.

التعرّف على قواعد الاحتفاظ التلقائية في المنصة

يمرّر Soong تلقائيًا قواعد الأساس العامة التالية إلى كل استدعاء لـ R8 على أهداف تجميع DEX (android_app وandroid_test ووحدات java_library القابلة للتثبيت)، والتي تم ضبطها في build/soong/java/dex.go:

  • build/make/core/proguard.flags: يحافظ على الفئات والأعضاء المُشار إليهم باستخدام @com.android.internal.annotations.VisibleForTesting في جميع الحزم، وباستخدام @VisibleForTesting (androidx.annotation.VisibleForTesting أو com.google.common.annotations.VisibleForTesting) ضمن حزم android.** وcom.android.** وcom.google.android.**. ويحافظ أيضًا على @TestApi و@Keep (androidx.annotation وandroid.support.annotation وcom.android.internal.annotations) و@KeepForWeakReference و@WeaklyReferencedCallback و@dalvik.annotation.optimization.**.
  • ‫build/make/core/proguard_basic_keeps.flags: يحافظ على سمات SourceFile لتتبُّع تسلسل استدعاء الدوال البرمجية، والتعليقات التوضيحية لظهور وقت التشغيل (RuntimeVisible*Annotations)، وسمات Exceptions وAnnotationDefault، وطُرق native، وأعضاء Serializable، وطُرق @JavascriptInterface، ومُنشئي Throwable(String)، وحقول Parcelable$CREATOR، وحقول protobuf MessageLite.
  • build/make/core/proguard/kotlin.flags: لإيقاف التحذيرات غير الضارة لتعليقات Kotlin التوضيحية الوصفية المحدّدة (kotlin.Metadata وkotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) وإزالة تعليقات Kotlin التوضيحية DebugMetadata في بنية الإصدار.
  • build/make/core/proguard/checknotnull.flags: يستبدل هذا الخيار طلبات المساعد الشائعة للتحقّق من القيم الفارغة (com.google.common.base.Preconditions.checkNotNull وdagger.internal.Preconditions.checkNotNull*) بعمليات تحقّق موجزة من القيم الفارغة في الرمز الثانوي. يتجاهل هذا الملف عن قصد Objects.requireNonNull للحفاظ على رسائل الاستثناء الصريحة عبر حدود واجهة برمجة التطبيقات لإطار العمل.
  • ‫build/make/core/proguard/enumvalues.flags: تحتفظ هذه القاعدة بالطريقتَين values وvalueOf في أنواع enum ما لم يتم إيقافها من خلال إعدادات التصميم.
  • القواعد التي يتم إنشاؤها تلقائيًا من خلال AAPT2: يفحص AAPT2 ملفات AndroidManifest.xml المدمجة وملفات XML للتنسيق وملفات XML المفضّلة لإنشاء قواعد دقيقة للاحتفاظ بكل من Activity وService وBroadcastReceiver وContentProvider وBackupAgent وApplication المسجّلة والفئة الفرعية المخصّصة View والفئة الفرعية Preference وطريقة XML android:onClick.

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

عند تدقيق ملفات proguard.flags أو keep.xml الحالية في مستودع النظام الأساسي، يجب تقييم كل قاعدة بالترتيب وفقًا للتسلسل الهرمي التالي المكوّن من أربع خطوات:

الخطوة 1: حذف القواعد المكرّرة أو القديمة

احذف القواعد التي تغطيها بالفعل المراجع الأساسية العامة، أو قواعد بيان AAPT2 والتنسيق، أو الإعدادات التلقائية لـ R8، بالإضافة إلى القواعد التي تشير إلى فئات أو حزم لم تعد موجودة.

إذا كانت جميع القواعد في ملف .flags مكرّرة، احذف الملف وأزِل proguard_flags_files من Android.bp. إذا كانت كتلة optimize المتبقية تعيد فقط ذكر الإعدادات التلقائية، يجب إزالة الكتلة المكررة وتنسيق ملف الإصدار باستخدام bpfmt -w Android.bp. عند تنظيف حزمة، تحقَّق مما إذا كانت الوحدات المصاحبة في الأدلة الفرعية (مثل صيغ استهداف Kotlin) تشير إلى ملف العلامات نفسه وعدِّلها معًا.

الخطوة 2: نقل قواعد الاحتفاظ بالبيانات المستندة إلى الاختبار

يمكنك نقل قواعد الاحتفاظ المستندة إلى الاختبار خارج ملفات .flags المخصّصة للإنتاج باستخدام أحد الأنماط التالية:

  • تتبُّع المراجع من أهداف الاختبار أو الأهداف المصاحبة (trace_references_from أو allow_traced_references): عندما يجمِّع كل من الهدف والوحدة التابعة المباشرة ملف DEX الخاص بهما (android_app أو android_test أو ملف dexed java_library) ويتم ربطهما من خلال instrumentation_for أو libs، يمكن لـ Soong تشغيل R8 tracereferences على الوحدة التابعة وإدخال قواعد الاحتفاظ التي تم إنشاؤها في تجميع R8 للهدف بدون إنشاء دورة تبعية أو طلب تقسيم يدوي لمكتبة .impl:

    • اختبارات المستودع نفسه (trace_references_from): يمكنك إدراج العناصر التابعة المباشرة لـ instrumentation_for أو libs في الوحدة المستهدَفة (يتم نشر ذلك أيضًا تلقائيًا في صيغ override_android_app وoverride_android_test، ويستمر في إتاحة مكتبات Java المصاحبة غير التابعة عند إقرانها بتقسيم .impl):

      android_app {
          name: "MyApp",
          srcs: ["src/**/*.java"],
          optimize: {
              obfuscate: true,
              shrink_resources: true,
              trace_references_from: ["MyAppTests"],
          },
      }
      
      android_test {
          name: "MyAppTests",
          instrumentation_for: "MyApp",
          srcs: ["tests/**/*.java"],
      }
      
    • الاختبارات على مستوى المستودعات أو المورّدين (allow_traced_references وtrace_references): عندما لا تتمكّن الوحدة المستهدَفة من إدراج الوحدة التابعة مباشرةً في trace_references_from (مثل الاختبارات في مستودع آخر أو شجرة المورّدين)، اضبط allow_traced_references: true على الوحدة المستهدَفة وفعِّلها من كل وحدة تابعة باستخدام trace_references: true:

      android_app {
          name: "MyApp",
          srcs: ["src/**/*.java"],
          optimize: {
              allow_traced_references: true,
          },
      }
      
      android_test {
          name: "MyAppVendorTests",
          instrumentation_for: "MyApp",
          srcs: ["tests/**/*.java"],
          optimize: {
              trace_references: true,
          },
      }
      
    • الاستخدام المستند إلى السلسلة في الاختبارات: تفحص أداة tracereferences مراجع مجموعة الثوابت في الرمز الثانوي ولا يمكنها رصد الاستخدام المستند إلى السلسلة في الاختبارات (Class.forName أو getDeclaredField أو getDeclaredMethod أو أدوات المساعدة في الحقن المباشر في المتغيرات). أضِف تعليقات توضيحية إلى الحقول والأساليب الإنتاجية التي يتم الوصول إليها بشكل انعكاسي باستخدام @VisibleForTesting (أو @UsedByReflection).

  • ربط مكتبة التنفيذ بالاختبارات (static_libs): إذا أردت عدم الاحتفاظ بنقاط دخول مخصّصة للاختبار فقط في حزمة APK للإصدار العلني وعدم الرغبة في أن تؤدي تغييرات الاختبار إلى إعادة تشغيل R8 على الهدف من الإصدار العلني، يمكنك وضع ملفات مصدر التطبيق في android_library (MyApp.impl) وربط MyApp.impl بشكل ثابت بكل من MyApp وMyAppTests:

    android_library {
        name: "MyApp.impl",
        srcs: ["src/**/*.java"],
        manifest: "AndroidManifest.xml",
    }
    
    android_app {
        name: "MyApp",
        static_libs: ["MyApp.impl"],
        optimize: {
            obfuscate: true,
            shrink_resources: true,
        },
    }
    
    android_test {
        name: "MyAppTests",
        srcs: ["tests/**/*.java"],
        static_libs: ["MyApp.impl"],
    }
    

    يتيح هذا النمط المكوّن من 3 وحدات تشغيل "MyAppTests" كاختبار لقياس حالة التطبيق ذاتي مع إمكانية الوصول الكامل إلى الفئات الداخلية مع السماح بتقليص "MyApp" وإخفائه تمامًا من نقاط الدخول إلى الإصدار العلني.

  • إضافة تعليقات توضيحية إلى عناصر الربط الاختبارية باستخدام @VisibleForTesting: إذا كان ملف APK الاختباري الخارجي يستدعي فقط بعض الطرق أو الدوال الإنشائية الداخلية في هدف android_app، يمكنك إضافة تعليقات توضيحية إلى هذه التعريفات باستخدام @VisibleForTesting. تحتفظ القاعدة الأساسية العالمية في build/make/core/proguard.flags بـ @VisibleForTesting عنصر في حزم android.** وcom.android.** وcom.google.android.** تلقائيًا بدون ملفات .flags مخصّصة. (بالنسبة إلى وحدات المورّدين خارج مساحات الأسماء هذه، استخدِم trace_references_from أو @UsedByReflection أو قواعد المكتبة التي تم تصديرها). عند إضافة تعليقات توضيحية يدويًا باستخدام @VisibleForTesting بدلاً من استخدام trace_references_from أو trace_references، يجب أيضًا إضافة تعليقات توضيحية إلى الفئات التي تم تمريرها إلى Mockito mock(...) أو spy(...) (وليس فقط الطرق المقتطعة)، لأنّ mockito-target (dexmaker-mockito) على الجهاز فقط ينشئ فئات فرعية لوقت التشغيل ويمكن لـ R8 وضع علامة على الفئات غير الفرعية final (MockitoException: Can't mock/spy class ... - final class).

  • تجنُّب المكتبات الثابتة المتداخلة أو إعادة تجميعها (jarjar_rules): عندما يرتبط كل من android_app وحزمة APK الاختبارية instrumentation_for بشكل ثابت بالمكتبة نفسها (مباشرةً، مثل guava، أو بشكل انتقالي)، يرى tracereferences تلك الفئات المحدّدة في الاختبار ولا يصدر أي قواعد احتفاظ للتطبيق، وفي وقت التشغيل، يحجب مُحمِّل الفئة الرئيسي للتطبيق نسخة الاختبار بفئاته التي تم تقليصها باستخدام R8، ما يتسبب في حدوث IllegalAccessError أو NoSuchMethodError. إذا لم يكن الاختبار بحاجة إلى نسخته المجمّعة، يمكنك إزالة النسخة المكرّرة من static_libs للاختبار (أو ربطها من خلال libs) لتجنُّب التعارض، أو إعادة تجميع حزم المكتبات الثابتة المتداخلة للتطبيق ضمن بادئة خاصة بالوحدة باستخدام jarjar_rules.

الخطوة 3: تصدير القواعد من المكتبة المالكة

إذا كانت مكتبة java_library أو android_library مشتركة تتطلّب قواعد احتفاظ من أجل الانعكاس الداخلي أو عمليات استدعاء JNI، حدِّد القواعد على هدف المكتبة واضبط export_proguard_flags_files: true:

java_library {
    name: "my-shared-library",
    srcs: ["src/**/*.java"],
    optimize: {
        proguard_flags_files: ["proguard.flags"],
        export_proguard_flags_files: true,
    },
}

جميع أهداف android_app والمكتبات اللاحقة التي ترتبط بشكل ثابت بـ my-shared-library تكتسب هذه القواعد تلقائيًا، لذا لا تحتاج التطبيقات اللاحقة إلى تكرارها. عندما تحتاج وحدات متعددة في أدلة مختلفة إلى مشاركة مجموعة قواعد غير مرتبطة بمكتبة رموز واحدة، يمكنك نشر ملف .flags من خلال filegroup صريح في Android.bp حتى تتمكّن الوحدات من الإشارة إلى :my-shared-flags بشكل واضح عبر حدود الحزمة. للحصول على إرشادات عامة حول إنشاء قواعد الاحتفاظ بمستهلكي المكتبة بدون تقييد تحسين التطبيقات النهائية، يُرجى الاطّلاع على التحسين لمؤلفي المكتبات.

الخطوة 4: إضافة تعليقات توضيحية إلى التعريفات في الرمز المصدري

عند الوصول إلى فئة أو طريقة أو دالة إنشائية أو حقل من خلال الانعكاس أو واجهة JNI ولم يتم تغطيتها بواسطة AAPT2 أو المراجع الأساسية العامة، استبدِل قواعد -keep المنفصلة في ملفات .flags بتعليقات توضيحية للمصدر من دليل الاحتفاظ بالتعليقات التوضيحية (راجِع مرجع Javadoc‏ keepanno):

  • @UsesReflection: يُفضَّل استخدام هذا التعليق التوضيحي عندما تتحكّم في الرمز البرمجي الذي ينفّذ الانعكاس. ضَعها في موقع استدعاء الانعكاس للإعلان عن الفئات أو الطرق أو الحقول المستهدَفة التي يتم الوصول إليها ديناميكيًا. بما أنّ @UsesReflection يرمّز تلقائيًا شرطًا مسبقًا بأنّ موقع الاستدعاء الذي تمّت إضافة تعليقات توضيحية إليه يمكن الوصول إليه، يزيل R8 كلاً من المتصل والهدف الانعكاسي إذا كان موقع الاستدعاء غير مستخدَم.
  • @UsedByReflection: يتم وضعها على الفئات أو الطرق أو الحقول أو الدوال الإنشائية التي يتم إنشاء مثيل لها أو استدعاؤها بشكل انعكاسي من خلال رمز أو مكتبات خارجية، مثل الفئات التي يتم تحميلها باستخدام Class.forName من مفاتيح Bundle أو Settings، أو فئات المكوّنات الإضافية التي يتم تحميلها من خلال أدوات تحميل الفئات الديناميكية. حدِّد kind (مثل KeepItemKind.CLASS_AND_METHODS) وpreconditions (@KeepCondition) وقيود المَعلمات لكي يحتفظ R8 بالعقد الانعكاسي الدقيق فقط ويظل بإمكانه تحسين أو إزالة العناصر غير المستخدَمة في الفئة. لا تضِف @UsedByReflection إلى المكوّنات المسجَّلة في ملف البيان، مثل JobService أو BroadcastReceiver، لأنّ أداة AAPT2 تحتفظ بها.
  • @UsedByNative: يمكنك وضعها على الطرق أو الحقول التي يتم الوصول إليها من رمز JNI بلغة C أو ++C باستخدام GetMethodID أو GetStaticMethodID أو GetFieldID.
  • @KeepForApi: يتم وضعها على فئات أو أعضاء واجهة برمجة التطبيقات للمكتبة التي يجب أن تظل سليمة عند تقليص حجم المكتبة نفسها قبل التوزيع.
  • ‫@Keep (androidx.annotation.Keep): استخدِم هذه السمة كبديل عندما لا تنطبق السمة keepanno. يؤدي تطبيق @Keep على فئة إلى الاحتفاظ بالفئة وجميع عناصرها بدون شروط. يؤدي تطبيق @Keep على طريقة أو حقل إلى إنشاء نقطة دخول غير مشروطة تحتفظ بالعضو والفئة التي تحتوي عليه حتى إذا لم يتم إنشاء مثيل للفئة مطلقًا، بينما يعبّر keepanno عن إمكانية الوصول المشروطة.

لاستخدام تعليقات R8 keepanno التوضيحية في وحدة Soong، اتّبِع الخطوات التالية:

  1. إضافة ""keepanno-annotations"" إلى "libs" في "Android.bp":

    libs: [
        "keepanno-annotations",
    ],
    
  2. استورِد com.android.tools.r8.keepanno.annotations.* وأضِف تعليقات توضيحية إلى الموقع الذي تم فيه الإعلان أو الاستدعاء في الرمز المصدر بلغة Java أو Kotlin:

    import com.android.tools.r8.keepanno.annotations.KeepItemKind;
    import com.android.tools.r8.keepanno.annotations.UsedByReflection;
    
    public final class CustomPluginController {
        @UsedByReflection(
            description = "Instantiated via Class.forName from plugin config",
            kind = KeepItemKind.CLASS_AND_METHODS)
        public CustomPluginController(Context context) {
            // ...
        }
    }
    

    تترجم أداة R8 تعليقات keepanno التوضيحية مباشرةً إلى نموذج قاعدة الاحتفاظ الداخلي وتزيل التعليقات التوضيحية من مخرجات DEX النهائية، ما يضيف صفرًا من أعباء الرمز الثانوي لوقت التشغيل.

تجنُّب الأخطاء الشائعة

توضّح الأقسام التالية الأخطاء الشائعة في proguard.flags وkeep.xml في رمز المنصة وكيفية حل مشكلة.

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

# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
  • سبب تأثير ذلك في الأداء: هذه القاعدة مكرّرة بالكامل مع أداة AAPT2. بما أنّ هذه القاعدة تستخدم حرف بدل بدون التحقّق مما إذا كان المكوّن مسجَّلاً في ملف AndroidManifest.xml النهائي المدمج، فإنّها تفرض على R8 الاحتفاظ بكل فئة فرعية من Activity أو Service أو BroadcastReceiver يتم العثور عليها في أي مكان في مسار الفئة، بما في ذلك مكوّنات المكتبة غير المستخدَمة وأنشطة تصحيح الأخطاء غير المفعَّلة.
  • الحلّ المقترَح: احذف القاعدة. تفحص أداة AAPT2 ملفات البيان المدمجة وتنشئ قواعد -keep دقيقة للعناصر المسجَّلة.

قواعد الاحتفاظ بمعالج النقر على XML العام

# Don't do this:
-keepclassmembers class * {
    public void *(android.view.View);
}
  • سبب التأثير السلبي في الأداء: يؤدي الاحتفاظ بكل طريقة public void *(View) في كل فئة في الوحدة إلى منع R8 من تجريد أو تضمين أي طريقة تقبل مَعلمة View واحدة.
  • الحلّ المقترَح: احذف القاعدة. يفحص AAPT2 ملفات XML للتنسيق وينشئ قواعد احتفاظ مستهدَفة للطرق التي تشير إليها سمات android:onClick.

قواعد الاحتفاظ بـ Broad View getter وsetter

# Don't do this:
-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
    public *** get*();
}
  • سبب التأثير السلبي في الأداء: تجبر هذه القاعدة أداة R8 على الاحتفاظ بكل دالة getter وsetter في كل فئة فرعية من View في التطبيق وجميع المكتبات المرتبطة (بما في ذلك مكتبات AndroidX وMaterial)، ما يمنع إزالة الدوال غير المستخدَمة والتضمين في جميع أجزاء رمز واجهة المستخدم.
  • الحلّ المقترَح: احذف القاعدة. تحتفظ أداة AAPT2 مسبقًا بالمنشئات لفئات View المخصّصة التي تم تضخيمها من ملفات XML للتنسيق. إذا كان الرمز يحرك سمة عرض باستخدام أسماء سلاسل انعكاسية مثل ObjectAnimator.ofFloat(view, "translationZ", ...)، استبدِل اسم السلسلة بمرجع سمة مكتوب (View.TRANSLATION_Z أو تنفيذ FloatProperty أو IntProperty مخصّص) لتجنُّب الانعكاس تمامًا. إذا لم يكن بالإمكان تجنُّب الوصول إلى السمة الانعكاسية، يمكنك إضافة تعليق توضيحي إلى دالة getter أو setter المحددة باستخدام @UsedByReflection.

عمليات التحسين أو التشويش الشاملة التي تؤدي إلى إيقاف الميزات

# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
  • سبب تأثير ذلك سلبًا في الأداء: يؤدي وضع -dontoptimize أو -dontshrink داخل ملف .flags إلى إلغاء إعدادات Android.bp للوحدة بدون إشعار وإيقاف عمليات التحسين في الهدف بالكامل. والأسوأ من ذلك، إذا كانت المكتبة تصدّر ملف .flags يحتوي على -dontoptimize، سيتم إيقاف تحسين R8 لكل android_app تابعة ترتبط بالمكتبة.
  • الحلّ المقترَح: أزِل -dontoptimize و-dontshrink و-dontobfuscate من ملفات .flags. يمكنك التحكّم في سلوك التحسين بشكل صريح في Android.bp باستخدام مجموعة optimize (shrink وoptimize وobfuscate) في الهدف النهائي.

علامات التشخيص في القواعد الملتزم بها

# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
  • سبب تأثيرها السلبي في عملية الإنشاء: تُغرق علامات التشخيص سجلات الإنشاء أو تحاول كتابة ملفات الإخراج في مسارات محلية أثناء عمليات إنشاء Soong في وضع الحماية.
  • الحلّ المقترَح: احذف هذه العلامات من ملفات .flags التي تم تنفيذها. يكتب Soong تلقائيًا مخرجات الاستخدام والربط في R8، مثل proguard_dictionary وproguard_usage.zip، في الدليل الوسيط للوحدة ضمن out/soong/.intermediates/.

قواعد الاحتفاظ بالرموز البرمجية الخاصة بالاختبار في الإصدار العلني

  • سبب تأثير ذلك سلبًا في الأداء: إنّ إضافة قواعد -keep مخصّصة فقط للوصول إلى الاختبار يؤدي إلى بقاء هذا الرمز البرمجي غير مخفي والاحتفاظ به في إصدارات الإنتاج. على الرغم من أنّ ضبط trace_references_from (أو allow_traced_references مع trace_references: true) أو إضافة تعليقات توضيحية للطرق باستخدام @VisibleForTesting يجنّب الاحتفاظ بقواعد -keep اليدوية ويحتفظ فقط بما تشير إليه الاختبارات، لا تزال هذه الرموز تُرسَل في الملف الثنائي للإنتاج.
  • الحلّ المقترَح: يمكنك إزالة قواعد -keep المخصّصة للاختبار من خلال تفعيل trace_references_from (أو allow_traced_references و trace_references: true)، أو تنظيم التطبيق في مكتبة .impl مرتبطة بحزمة APK لاختبار لقياس حالة التطبيق باستخدام static_libs لإبقاء نقاط الدخول المخصّصة للاختبار فقط خارج حزمة APK للإصدار العلني تمامًا، كما هو موضّح في الخطوة 2: نقل قواعد الاحتفاظ المستندة إلى الاختبار.

أحرف بدل الموارد الشاملة في keep.xml

<!-- Don't do this in res/raw/keep.xml: -->
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@raw/*,@drawable/*,@string/*" />
  • سبب تأثير ذلك سلبًا في الأداء: تحتفظ أحرف البدل الشاملة بكل مورد في نوع المطابقة في جميع أنحاء الوحدة والتبعيات الخاصة بها، ما يؤدي إلى إبطال عملية تقليص الموارد (shrink_resources: true) وتضخيم حزمة APK وجدول resources.arsc الذي تم ربطه.
  • الحلّ المقترَح:
    • تفضيل مراجع الموارد الثابتة على keep.xml: تجنَّب استخدام طريقة Resources.getIdentifier للبحث عن مجموعة محدودة من الموارد بشكل ديناميكي، مثل سلاسل التجارب المرقّمة أو العناصر القابلة للرسم ذات المظاهر. بدلاً من ذلك، استخدِم عبارة switch في وقت التجميع أو خريطة على ثوابت R.id أو R.string أو R.drawable الثابتة. تتيح المراجع الثابتة لـ R8 وAAPT2 تتبُّع الموارد المباشرة بدقة، والتخلص من أعباء البحث عن السلاسل في وقت التشغيل، وإزالة الحاجة إلى keep.xml تمامًا.
    • إدراج أسماء موارد محدّدة: إذا كان البحث الديناميكي مطلوبًا، مثلاً من خلال قارئ تراخيص تابع لجهة خارجية، أدرِج معرّفات الموارد المحدّدة في tools:keep (مثلاً، tools:keep="@raw/third_party_licenses").
    • حذف ملفات keep.xml المكرّرة: إذا كانت الموارد المدرَجة مُشار إليها بشكل ثابت في الرمز (R.raw.foo) أو ملف XML‏ (@raw/foo)، تحتفظ أداة تقليص حجم الموارد بها تلقائيًا، لذا يمكنك حذف res/raw/keep.xml.

التحقّق من تغييرات القواعد وتدقيقها

عند إزالة قواعد الاحتفاظ أو تضييق نطاقها في proguard.flags أو keep.xml، يُرجى التأكّد من أنّ الوحدة يتم إنشاؤها بشكل صحيح، وتجتاز اختبارات الوحدة واختبار لقياس حالة التطبيق، وتحتفظ بجميع نقاط الدخول المطلوبة.

إنشاء الوحدات واختبارها

  1. أنشئ الوحدة بشكل نظيف للتحقّق من اكتمال R8 وAAPT2 بدون تحذيرات المراجع المفقودة:

    m <MODULE_NAME>
    
  2. شغِّل اختبارات الوحدة واختبارات قياس حالة التطبيق للوحدة باستخدام atest:

    atest <TEST_MODULE_NAME>
    
  3. بالنسبة إلى تطبيقات النظام أو الخدمات المميّزة أو الوحدات التي تعتمد على الأجهزة، يمكنك إجراء اختبارات قياس الأداء وواجهة المستخدم على الأجهزة الفعلية المستهدفة أو في مختبر اختبار الأجهزة لتجربة مسارات الانعكاس في وقت التشغيل وعمليات ربط الاتصال بين العمليات (IPC) وتضخيم الموارد.

فحص ملفات DEX والاختلافات في الموارد

قارِن بين حزمة APK أو ملف JAR المجمَّعَين قبل تغييرات القواعد وبعدها للتأكّد من أنّ R8 يزيل الرموز البرمجية والموارد غير المستخدَمة بدون إزالة نقاط الدخول المتوقَّعة:

  • استخدِم apkanalyzer أو dexdump لفحص الفئات والطُرق والحقول المحفوظة في ملفات APK أو DEX الناتجة:

    apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
    
  • عند تعديل keep.xml أو تفعيل shrink_resources، استخدِم aapt2 dump resources للتأكّد من أنّ عملية الإنشاء تزيل الموارد غير المستخدَمة وتحتفظ بالموارد المطلوبة:

    aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
    
  • عند تشغيل prebuilts/r8/r8.jar مباشرةً على classes.jar قبل R8 للوحدة للتحقّق من تغييرات العلامات بلا إنترنت بدون إنشاء نظام أساسي كامل، يجب تمرير كل من ملفات JAR لمسار فئة --lib للوحدة (android.jar وframework.jar وlibs المشتركة) وقواعد proguard.options التي تم إنشاؤها بواسطة AAPT2 من AndroidManifest.xml وملفات XML للتنسيق. بدون هذه المدخلات، لا يمكن لأداة R8 حلّ الأنواع الفائقة لإطار العمل (Activity وService وPhone وHandler) وطرق التدرّج الهرمي غير المحتفظ بها.

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

يتضمّن برنامج التجميع R8 المفتوح المصدر أداة تحليل Keep Radius (المعروضة أيضًا في "استوديو Android" وGradle باسم أداة تحليل إعدادات R8) التي تقيس التأثير الدقيق لكل قاعدة احتفاظ بالبيانات أثناء التجميع. يدمج Soong هذا المحلّل مباشرةً في إصدار نظام Android الأساسي (الذي تم ضبطه في build/soong/java/dex.go).

لتحليل قواعد الاحتفاظ بالبيانات لوحدة نظام أساسي أو في عملية إنشاء كاملة:

  1. شغِّل عملية الإنشاء باستخدام متغيّر البيئة R8_DUMP_KEEP_RADIUS=true:

    R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>
    

    عند ضبط R8_DUMP_KEEP_RADIUS=true، يوجّه Soong أداة R8 لتسجيل مقاييس قواعد الاحتفاظ في ملف r8keepradius.pb وسيط لكل وحدة برمجية مجمّعة ضمن out/soong/.intermediates/. بالنسبة إلى كل قاعدة احتفاظ بالبيانات وتعليق توضيحي من النوع keepanno، يسجّل R8 ما يلي:

    • نطاق الاحتفاظ الفوري: الفئات والحقول والطُرق المحدّدة التي تحتفظ بها القاعدة، بالإضافة إلى القيود المحدّدة التي تفرضها على تقليص الحجم أو التحسين أو التعتيم.
    • تضمين القواعد: ما هي قواعد الاحتفاظ بالبيانات أو التعليقات التوضيحية الأخرى التي تحتفظ بالعناصر نفسها؟ إذا كانت القاعدة المخصّصة تندرج بالكامل ضمن قاعدة AAPT2 أو قاعدة أساسية عامة، يمكنك حذفها بأمان.
    • القواعد على مستوى الحزمة والقواعد العامة: هي القواعد التي تستخدم أحرف بدل واسعة على مستوى الحزمة أو تطبّق توجيهات الضبط العامة.
  2. حوِّل مخرجات r8keepradius.pb إلى تقرير HTML تفاعلي باستخدام KeepRadiusHtmlReportGenerator (المضمَّنة في prebuilts/r8/r8.jar):

    # Generate an HTML report for a single module
    INTERMEDIATES=out/soong/.intermediates/packages/apps/<APP_NAME>/<APP_NAME>
    java -cp prebuilts/r8/r8.jar \
        com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \
        $INTERMEDIATES/android_common/r8keepradius.pb \
        keep_radius_report.html
    
    # Scan all built modules under out/soong/.intermediates and generate
    # per-module HTML reports plus an aggregate keepradius.html summary
    java -cp prebuilts/r8/r8.jar \
        com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \
        out/soong/.intermediates \
        out/keep_radius_reports
    

    عند إدخال دليل، يتصفّح KeepRadiusHtmlReportGenerator جميع ملفات *keepradius*.pb، وينشئ تقرير HTML لكل وحدة، وينشئ out/keep_radius_reports/keepradius.html يلخّص عدد العناصر النشطة وعدد العناصر المحفوظة وقواعد الحفظ ذات النطاق الأكبر في عملية الإنشاء. لمزيد من المعلومات حول تفسير نتائج تقليص الحجم والتحسين والتمويه في التقرير الذي تم إنشاؤه، يُرجى الاطّلاع على استخدام أداة تحليل إعدادات R8.

يتيح Soong أيضًا استخدام متغيّرات بيئة إضافية لتشخيص R8 والحلقة الداخلية في build/soong/java/dex.go:

  • ‫R8_DUMP_INPUT=true: يكتب r8inputs.zip في الدليل الوسيط للوحدة الذي يحتوي على جميع ملفات JAR للإدخال وملفات JAR للمكتبة وإعدادات ProGuard المدمجة لإعادة إنتاج R8 المستقلة.
  • R8_DUMP_PERFETTO_TRACE=true: يكتب r8trace.ptrace في الدليل الوسيط للوحدة لفحص عمليات تجميع R8 في Perfetto.
  • ‫SOONG_SKIP_TRACE_REFERENCES=true: يتخطّى تشغيل قواعد tracereferences التي تم ضبطها من قِبل trace_references_from أو trace_references لتسريع عمليات إنشاء الحلقة الداخلية المحلية عند تكرار رمز الاختبار.

التحقّق من صحة قواعد مستهلكي المكتبة

عندما تصدّر java_library أو android_library قواعد الاحتفاظ إلى المستهلكين النهائيين (export_proguard_flags_files: true)، يجب ألا تتضمّن هذه القواعد علامات عامة تغيّر أو توقف التحسين للتطبيق المستهلك.

يوفّر R8 أداة تحليل مفتوحة المصدر لقواعد الاحتفاظ بالبيانات (ProcessKeepRules)، وهي متاحة في إصدار نظام Android الأساسي كأداة مضيفة process-keep-rules (محدّدة في prebuilts/r8/Android.bp):

# Build the R8 keep rules validator host binary
m process-keep-rules

# Validate one or more ProGuard configuration files
out/host/linux-x86/bin/process-keep-rules <PROGUARD_FLAGS_PATH>

تحلّل أداة process-keep-rules كل ملف إعداد وتتعطّل مع تشخيصات الملف والسطر إذا واجهت توجيهات غير مسموح بها في قواعد مستهلك المكتبة. تشمل التوجيهات غير المسموح بها التحسين العام أو تقليص الحجم أو إيقاف التشويش (مثل -dontoptimize أو -dontshrink) وإعادة تجميع الحِزم وعلامات تعديل الوصول وعلامات التشخيص أو الربط و-keepattributes على مستوى التطبيق.

تدقيق أشجار المصدر باستخدام pgaudit.py

لتدقيق أدلة ملفات المصدر غير المجمّعة إلى جانب أدوات تحليل وقت الإنشاء في R8، تتضمّن شجرة نظام Android الأساسي النص البرمجي pgaudit.py ضمن build/make/core/proguard/tools/. تفحص أداة التحليل الثابتة هذه ملفات Android.bp وproguard.flags وkeep.xml في المستودع للإشارة إلى القواعد التي سبق أن غطّتها أداة AAPT2 أو المرجعيات الأساسية للمنصة العالمية، وأحرف البدل الواسعة، وعمليات إلغاء -dontoptimize، وقواعد الاحتفاظ المخصّصة للاختبار فقط:

# Audit a single package directory
./build/make/core/proguard/tools/pgaudit.py packages/apps/Provision/

# Scan a subsystem tree and write a structured JSON report
./build/make/core/proguard/tools/pgaudit.py packages/ --json=audit_report.json

يمكن لفِرق المنصات والمصنّعين الأصليين للأجهزة الجمع بين pgaudit.py لإجراء فحص سريع لشجرة المصدر، وprocess-keep-rules للتحقّق من صحة قواعد المكتبة التي تم تصديرها، وR8_DUMP_KEEP_RADIUS=true مع KeepRadiusHtmlReportGenerator لقياس نطاق الاحتفاظ الدقيق بالفئات والحقول والطرق لكل قاعدة في عملية الإنشاء.