يُشغّل نظام تصميم 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_serverclasspath: إنّ الوحدات (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أو JNIFindClassوGetMethodID) بدون قواعد الاحتفاظ أو التعليقات التوضيحية، يُعيد التشويش تسمية هذه الأهداف ويوقف عمليات البحث في وقت التشغيل. (يُرجى العِلم أنّ الانعكاس غير المُعلَّق يفشل أيضًا فيshrink: trueوoptimize: true). يمكنك إضافة تعليقات توضيحية إلى نقاط الدخول هذه باستخدام دليل الاحتفاظ بالتعليقات التوضيحية (@UsesReflectionأو@UsedByReflectionأو@UsedByNative) لكي يحتفظ R8 بأسمائها ويخفي بقية الوحدة.
اتّباع مبدأ عدم استخدام علامات مخصّصة
لا تحتوي وحدة نظام Android الأساسي المثالية على ملفات proguard.flags أو keep.xml مخصّصة. في إصدار نظام Android الأساسي، تكون الغالبية العظمى من قواعد الاحتفاظ المخصّصة
زائدة عن الحاجة:
- الأساسيات في المنصة وأداة AAPT2: يتم الاحتفاظ بمعظم نقاط الدخول تلقائيًا من خلال الأساسيات العامة في المنصة (مثل
@Keepوطرق JNInativeو@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، وحقول protobufMessageLite. 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وطريقة XMLandroid: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أو ملف dexedjava_library) ويتم ربطهما من خلالinstrumentation_forأوlibs، يمكن لـ Soong تشغيل R8tracereferencesعلى الوحدة التابعة وإدخال قواعد الاحتفاظ التي تم إنشاؤها في تجميع 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، يجب أيضًا إضافة تعليقات توضيحية إلى الفئات التي تم تمريرها إلى Mockitomock(...)أو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، اتّبِع الخطوات التالية:
إضافة "
"keepanno-annotations"" إلى "libs" في "Android.bp":libs: [ "keepanno-annotations", ],استورِد
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، يُرجى التأكّد من أنّ الوحدة يتم إنشاؤها بشكل صحيح، وتجتاز اختبارات الوحدة واختبار لقياس حالة التطبيق، وتحتفظ بجميع نقاط الدخول المطلوبة.
إنشاء الوحدات واختبارها
أنشئ الوحدة بشكل نظيف للتحقّق من اكتمال R8 وAAPT2 بدون تحذيرات المراجع المفقودة:
m <MODULE_NAME>شغِّل اختبارات الوحدة واختبارات قياس حالة التطبيق للوحدة باستخدام
atest:atest <TEST_MODULE_NAME>بالنسبة إلى تطبيقات النظام أو الخدمات المميّزة أو الوحدات التي تعتمد على الأجهزة، يمكنك إجراء اختبارات قياس الأداء وواجهة المستخدم على الأجهزة الفعلية المستهدفة أو في مختبر اختبار الأجهزة لتجربة مسارات الانعكاس في وقت التشغيل وعمليات ربط الاتصال بين العمليات (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).
لتحليل قواعد الاحتفاظ بالبيانات لوحدة نظام أساسي أو في عملية إنشاء كاملة:
شغِّل عملية الإنشاء باستخدام متغيّر البيئة
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 أو قاعدة أساسية عامة، يمكنك حذفها بأمان.
- القواعد على مستوى الحزمة والقواعد العامة: هي القواعد التي تستخدم أحرف بدل واسعة على مستوى الحزمة أو تطبّق توجيهات الضبط العامة.
حوِّل مخرجات
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 لقياس
نطاق الاحتفاظ الدقيق بالفئات والحقول والطرق لكل قاعدة في عملية الإنشاء.