R8 की मदद से, प्लैटफ़ॉर्म कोड और संसाधनों को ऑप्टिमाइज़ करना

Android प्लैटफ़ॉर्म का बिल्ड सिस्टम (Soong), DEX बाइटकोड को कंपाइल करने वाले टारगेट पर R8 कंपाइलर चलाता है. इनमें 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 के कम तरीकों का मतलब है कि dex2oat, बिल्ड-टाइम या उपयोगकर्ता के डिवाइस पर कंपाइल करने के दौरान .odex और .vdex फ़ाइलें जनरेट करता है.
  • रंटाइम मेमोरी पर कम दबाव: Android, .odex, .vdex, और APK संसाधन टेबल को मांग के हिसाब से पेजिंग mmap कॉल का इस्तेमाल करके प्रोसेस मेमोरी में मैप करता है. इसलिए, छोटे बाइनरी से ऐप्लिकेशन के स्टार्टअप के दौरान पेज फ़ॉल्ट कम होते हैं. साथ ही, प्रोसेस में मौजूद कोड और संसाधन मेमोरी कम होती है. कंपाइल किए गए ऐप्लिकेशन कोड से डिवाइस की मेमोरी पर पड़ने वाले असर के बारे में ज़्यादा जानने के लिए, ऐप्लिकेशन कोड, मेमोरी है लेख पढ़ें.
  • पूरे प्रोग्राम को ज़्यादा असरदार तरीके से ऑप्टिमाइज़ करना: कीप की सीमाएं कम होने से, R8 को इनलाइन तरीके से काम करने वाली मेथड, इंटरफ़ेस और वर्चुअल कॉल को डीवर्चुअलाइज़ करने, इस्तेमाल न किए गए फ़ील्ड को हटाने, और क्लास में कॉन्स्टेंट को ट्रांसफ़र करने की सुविधा मिलती है.

Soong में R8 ऑप्टिमाइज़ेशन को कॉन्फ़िगर करना

Android.bp फ़ाइलों में, R8 ऑप्टिमाइज़ेशन को कॉन्फ़िगर करने के लिए, android_app और इंस्टॉल किए जा सकने वाले java_library टारगेट पर optimize प्रॉपर्टी ब्लॉक का इस्तेमाल करें. इसके अलावा, उपभोक्ता के लिए 'कीप रूल' एक्सपोर्ट करने के लिए, android_library और स्टैटिक java_library मॉड्यूल पर भी इसका इस्तेमाल किया जा सकता है:

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

यहां दी गई टेबल में, Soong में इस्तेमाल होने वाली सबसे आम optimize प्रॉपर्टी के बारे में खास जानकारी दी गई है. इन्हें build/soong/java/dex.go में तय किया गया है:

प्रॉपर्टी ब्यौरा
enabled यह विकल्प कंट्रोल करता है कि टारगेट पर R8 काम करेगा या नहीं. सभी android_app टारगेट के लिए, डिफ़ॉल्ट रूप से true पर सेट होता है.
shrink यह विकल्प, ट्री शेकिंग को कंट्रोल करता है. इससे उन क्लास, फ़ील्ड, और तरीकों को हटाया जाता है जिन तक नहीं पहुंचा जा सकता. android_app टारगेट के लिए, डिफ़ॉल्ट रूप से true होता है. हालांकि, स्टैंडअलोन java_library और टेस्ट मॉड्यूल के लिए, यह false होता है. ये मॉड्यूल, -dontshrink पास करते हैं, जब तक कि इन्हें साफ़ तौर पर सेट न किया जाए.
optimize यह बाइटकोड ऑप्टिमाइज़ेशन को कंट्रोल करता है. जैसे, मेथड इनलाइनिंग, क्लास मर्जिंग, कॉन्स्टेंट प्रोपगेशन, और डेड ब्रांच रिमूवल. डिफ़ॉल्ट रूप से, यह android_app टारगेट के लिए true पर सेट होता है. इसे RELEASE_R8_OPTIMIZE_BY_DEFAULT रिलीज़ के लिए तैयार बिल्ड फ़्लैग से कंट्रोल किया जाता है.
obfuscate यह आइडेंटिफ़ायर को छोटा करने और उसका नाम बदलने की प्रोसेस को कंट्रोल करता है. यह डिफ़ॉल्ट रूप से false पर सेट होता है, ताकि यह पुराने वर्शन के साथ काम कर सके. हालांकि, इसे true पर सेट करें, ताकि DEX का साइज़ कम किया जा सके और ज़्यादा बेहतर ऑप्टिमाइज़ेशन किया जा सके (जहां भी हो सके, अस्पष्ट बनाने की सुविधा चालू करें देखें).
shrink_resources यह विकल्प, कोड को छोटा करने के बाद पैकेज किए गए APK से, इस्तेमाल न होने वाले संसाधनों (res/ एंट्री) को हटाता है. डिफ़ॉल्ट रूप से, इसकी वैल्यू 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 यह Java लाइब्रेरी के उन कंपैनियन टारगेट की सूची बनाता है जिनके बाइटकोड, इस टारगेट में रेफ़रंस देते हैं. R8, इन बाइटकोड को अपने-आप ट्रेस करता है और बनाए रखता है.

जहां भी हो सके, कोड को अस्पष्ट बनाने की सुविधा चालू करें

Soong में, पुराने वर्शन के साथ काम करने की सुविधा के लिए, obfuscate डिफ़ॉल्ट रूप से false पर सेट होता है. हालांकि, जहां भी हो सके वहां android_app और स्टैंडअलोन DEX टारगेट पर obfuscate: true को साफ़ तौर पर सेट करना चाहिए:

  • छोटा DEX और .vdex फ़ुटप्रिंट: पैकेज, क्लास, फ़ील्ड, और मेथड के नाम बदलकर छोटे आइडेंटिफ़ायर (a, b) करने से, DEX स्ट्रिंग पूल (string_ids और string_data_item) और टाइप डिस्क्रिप्टर छोटे हो जाते हैं. Android, प्रोसेस मेमोरी में .vdex और .odex फ़ाइलों को मैप करता है. इसलिए, छोटी सिंबल टेबल से सिस्टम पार्टीशन का साइज़ और रनटाइम मेमोरी फ़ुटप्रिंट, दोनों कम हो जाते हैं.
  • पूरे प्रोग्राम को ज़्यादा बेहतर तरीके से ऑप्टिमाइज़ करना: R8 को आइडेंटिफ़ायर का नाम बदलने की अनुमति देने से, क्लास मर्ज करने, पैकेज फ़्लैट करने, और सिंथेटिक क्लास को डुप्लीकेट होने से बचाने की सुविधा मिलती है. ऐसा उन पैकेज के लिए होता है जिन्हें R8 को नाम के टकराव से बचने के लिए छोड़ना पड़ता है.
  • पूरे स्टैक ट्रेस का सिंबोलाइज़ेशन: Soong में अस्पष्टता को चालू करने से, डीबग करने की क्षमता कम नहीं होती. R8 से कंपाइल किए गए हर टारगेट के लिए, Soong मॉड्यूल की इंटरमीडिएट डायरेक्ट्री में proguard_dictionary मैपिंग फ़ाइल बनाता है. साथ ही, सभी मॉड्यूल डिक्शनरी को बिल्ड के proguard-dict.zip आर्टफ़ैक्ट में बंडल करता है. इसके अलावा, यह DEX हेडर में मैपिंग हैश (--map-id-template) एम्बेड करता है और क्लास SourceFile एट्रिब्यूट (--source-file-template) को फिर से लिखता है, ताकि retrace स्टैक ट्रेस को अपने-आप सिंबोलाइज़ कर सके.

obfuscate: false को सिर्फ़ इन मामलों में इस्तेमाल करें (या सार्वजनिक एपीआई के नामों को साफ़ तौर पर सुरक्षित रखें):

  • बूटक्लाथपाथ या system_server क्लासपाथ पर शेयर की गई लाइब्रेरी: मॉड्यूल (java_library या java_sdk_library) जो बाहरी एपीआई को दिखाते हैं जिन्हें रनटाइम के दौरान अन्य मॉड्यूल से डाइनैमिक तरीके से लिंक किया जाता है (जैसे कि framework.jar, services.jar या <uses-library> टारगेट) को या तो obfuscate: false को बनाए रखना होगा या @KeepForApi या -keep नियमों (बूटक्लाथपाथ टारगेट के लिए protect_api_surface: true के साथ) का इस्तेमाल करके, अपने एपीआई को साफ़ तौर पर बनाए रखना होगा, ताकि कॉल करने वाले लोग, रनटाइम के दौरान अपने स्टब के ख़िलाफ़ कंपाइल किए गए क्लास और मेंबर के नामों को हल कर सकें.

किसी ऐप्लिकेशन पर obfuscate: true की सुविधा चालू करने से पहले, माइग्रेशन से जुड़ी इन ज़रूरी शर्तों की पुष्टि करें:

  • बाहरी टेस्ट APK की डिपेंडेंसी: अगर कोई बाहरी android_test APK सेट instrumentation_for: "MyApp", MyApp की इंटरनल क्लास या तरीकों को सीधे तौर पर इनवोक करता है, तो उन सिंबल का नाम बदलने से, टेस्ट रनटाइम में NoSuchMethodError या NoClassDefFoundError में गड़बड़ी होती है. टेस्ट को ऐसे APK के तौर पर स्ट्रक्चर करें जो खुद को इंस्ट्रुमेंट करता है और MyApp.impl को स्टैटिक तौर पर लिंक करता है. टेस्ट हुक को @VisibleForTesting से एनोटेट करें या trace_references_from को कॉन्फ़िगर करें (दूसरा चरण: टेस्ट-ड्रिवन कीप नियमों को माइग्रेट करना देखें), ताकि टेस्ट के लिए ज़रूरी इंटरनल सिंबल सुरक्षित रहें और ऐप्लिकेशन के बाकी हिस्से को अस्पष्ट किया जा सके.
  • स्ट्रिंग पर आधारित रिफ़्लेक्शन और 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 और लेआउट रिसॉर्स से जनरेट करता है (प्लैटफ़ॉर्म के डिफ़ॉल्ट कीप नियमों के बारे में जानें देखें).
  • टारगेट किए गए विकल्प: जहां एंट्री पॉइंट को सुरक्षित रखना ज़रूरी है वहां अलग की गई .flags फ़ाइलों के बजाय, एलान वाली साइट के एनोटेशन (keepanno, @VisibleForTesting), एक्सपोर्ट किए गए लाइब्रेरी के नियम (export_proguard_flags_files: true) या स्टैटिक टेस्ट लिंकिंग का इस्तेमाल करें. (मौजूदा कीप नियमों का ऑडिट करना और उन्हें माइग्रेट करना लेख देखें).

कस्टम proguard.flags या keep.xml फ़ाइल जोड़ने या बनाए रखने से पहले, पुष्टि करें कि नियम को डिफ़ॉल्ट बेसलाइन से पहले ही हैंडल किया जा चुका है या इसे कोड एनोटेशन पर माइग्रेट किया जा सकता है.

डिफ़ॉल्ट प्लैटफ़ॉर्म के निजी डेटा के रखरखाव के नियमों के बारे में जानकारी

इसलिए, Soong, build/soong/java/dex.go में कॉन्फ़िगर किए गए DEX-कंपाइलिंग टारगेट (android_app, android_test, और इंस्टॉल किए जा सकने वाले java_library मॉड्यूल) पर, R8 के हर इनवोकेशन को ये ग्लोबल बेसलाइन नियम अपने-आप पास कर देता है:

  • build/make/core/proguard.flags: यह सभी पैकेज में, @com.android.internal.annotations.VisibleForTesting के साथ एनोटेट की गई क्लास और सदस्यों को सुरक्षित रखता है. साथ ही, android.**, com.android.**, और com.google.android.** पैकेज में @VisibleForTesting (androidx.annotation.VisibleForTesting या com.google.common.annotations.VisibleForTesting) के साथ एनोटेट की गई क्लास और सदस्यों को सुरक्षित रखता है. यह @TestApi, @Keep (androidx.annotation, android.support.annotation, com.android.internal.annotations), @KeepForWeakReference, @WeaklyReferencedCallback, और @dalvik.annotation.optimization.** को भी सुरक्षित रखता है.
  • build/make/core/proguard_basic_keeps.flags: यह स्टैक ट्रेस, रनटाइम विज़िबिलिटी एनोटेशन (RuntimeVisible*Annotations), Exceptions और AnnotationDefault एट्रिब्यूट, native तरीके, Serializable मेंबर, @JavascriptInterface तरीके, Throwable(String) कंस्ट्रक्टर, Parcelable$CREATOR फ़ील्ड, और प्रोटोबफ़ MessageLite फ़ील्ड के लिए SourceFile एट्रिब्यूट को सुरक्षित रखता है.
  • build/make/core/proguard/kotlin.flags: यह Kotlin की कुछ मेटा-एनोटेशन (kotlin.Metadata और kotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) के लिए, बिना नुकसान वाली चेतावनियों को साइलेंट करता है. साथ ही, रिलीज़ बिल्ड में Kotlin DebugMetadata एनोटेशन हटाता है.
  • build/make/core/proguard/checknotnull.flags: यह सामान्य null चेक हेल्पर कॉल (com.google.common.base.Preconditions.checkNotNull और dagger.internal.Preconditions.checkNotNull*) को, छोटे बाइटकोड वाले null चेक से बदलता है. इस फ़ाइल में जान-बूझकर Objects.requireNonNull को शामिल नहीं किया गया है, ताकि फ़्रेमवर्क एपीआई की सीमाओं के बीच, अपवाद के मैसेज को सुरक्षित रखा जा सके.
  • build/make/core/proguard/enumvalues.flags: यह enum टाइप पर values और valueOf तरीकों को बनाए रखता है. हालांकि, ऐसा तब तक होता है, जब तक कि बिल्ड कॉन्फ़िगरेशन से इन्हें बंद न कर दिया जाए.
  • AAPT2 के अपने-आप जनरेट होने वाले नियम: AAPT2, मर्ज की गई AndroidManifest.xml, लेआउट एक्सएमएल, और प्राथमिकता वाली एक्सएमएल फ़ाइलों की जांच करता है. इससे हर रजिस्टर किए गए Activity, Service, BroadcastReceiver, ContentProvider, BackupAgent, Application, कस्टम View सबक्लास, Preference सबक्लास, और एक्सएमएल android:onClick तरीके के लिए, सटीक कीप नियम जनरेट किए जा सकते हैं.

मौजूदा डेटा को सुरक्षित रखने के नियमों का ऑडिट करना और उन्हें माइग्रेट करना

किसी प्लैटफ़ॉर्म रिपॉज़िटरी में मौजूद proguard.flags या keep.xml फ़ाइलों की ऑडिट करते समय, हर नियम का आकलन क्रम से करें. इसके लिए, चार चरणों वाली इस क्रम का पालन करें:

पहला चरण: काम के न रहे या पुराने नियम मिटाना

ऐसे नियमों को मिटाएं जो पहले से ही ग्लोबल बेसलाइन, AAPT2 मेनिफ़ेस्ट, लेआउट के नियमों या R8 के डिफ़ॉल्ट नियमों के तहत आते हैं. साथ ही, उन नियमों को भी मिटाएं जो अब मौजूद नहीं हैं.

अगर किसी .flags फ़ाइल में मौजूद सभी नियम काम के नहीं हैं, तो फ़ाइल मिटाएं और Android.bp से proguard_flags_files हटाएं. अगर बचे हुए optimize ब्लॉक में सिर्फ़ डिफ़ॉल्ट सेटिंग के बारे में बताया गया है, तो फ़ालतू ब्लॉक को हटाएं और बिल्ड फ़ाइल को bpfmt -w Android.bp के साथ फ़ॉर्मैट करें. किसी पैकेज को क्लीन अप करते समय, देखें कि सबडायरेक्ट्री में मौजूद कंपैनियन मॉड्यूल (जैसे, Kotlin टारगेट वैरिएंट) एक ही फ़्लैग फ़ाइल को रेफ़रंस करते हैं या नहीं. अगर ऐसा है, तो उन्हें एक साथ अपडेट करें.

दूसरा चरण: टेस्ट-ड्राइव के दौरान बनाए गए कीप के नियमों को माइग्रेट करना

टेस्ट-ड्रिवन कीप नियमों को कस्टम प्रोडक्शन .flags फ़ाइलों से बाहर ले जाएं. इसके लिए, इनमें से किसी एक पैटर्न का इस्तेमाल करें:

  • टेस्ट में लागू करने वाली लाइब्रेरी को लिंक करें (static_libs): जब कोई android_test, किसी ऐप्लिकेशन की पैकेज-प्राइवेट या इंटरनल जानकारी का इस्तेमाल करता है, तो सुझाया गया प्लैटफ़ॉर्म आर्किटेक्चर यह है कि ऐप्लिकेशन की सोर्स फ़ाइलों को 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"],
    }
    

    तीन मॉड्यूल वाले इस पैटर्न की मदद से, MyAppTests को इंटरनल क्लास के पूरे ऐक्सेस के साथ, खुद को इंस्ट्रुमेंट करने वाले टेस्ट के तौर पर चलाया जा सकता है. साथ ही, MyApp को टेस्ट में रुकावट डाले बिना, obfuscate: true को आसानी से चालू किया जा सकता है. इससे प्रोडक्शन APK में, सिर्फ़ टेस्ट के लिए एंट्री पॉइंट शिप करने से बचा जा सकता है. साथ ही, टेस्ट कोड में बदलाव होने पर, MyApp को फिर से बनाने की ज़रूरत नहीं पड़ती.

  • टेस्ट हुक को @VisibleForTesting के साथ एनोटेट करें: अगर कोई बाहरी टेस्ट APK, android_app टारगेट पर इंटरनल तरीकों या कंस्ट्रक्टर की कम संख्या को कॉल करता है और .impl लाइब्रेरी में फिर से स्ट्रक्चर करना मुश्किल है, तो उन घोषणाओं को @VisibleForTesting के साथ एनोटेट करें. ग्लोबल build/make/core/proguard.flags बेसलाइन में, android.**, com.android.**, और com.google.android.** पैकेज में मौजूद @VisibleForTesting आइटम अपने-आप सेव हो जाते हैं. इसके लिए, कस्टम .flags फ़ाइलों की ज़रूरत नहीं होती. (इन नेमस्पेस के बाहर मौजूद वेंडर मॉड्यूल के लिए, @UsedByReflection या एक्सपोर्ट किए गए लाइब्रेरी नियमों का इस्तेमाल करें.)

  • प्लेटफ़ॉर्म लाइब्रेरी की सीमाओं के पार trace_references_from का इस्तेमाल करें: जब टेस्ट, टारगेट इम्प्लीमेंटेशन को स्टैटिक तौर पर लिंक नहीं कर पाते हैं, तब टारगेट मॉड्यूल पर trace_references_from कॉन्फ़िगर करें. उदाहरण के लिए, सिस्टम सर्वर सेवाओं या framework-connectivity जैसे प्लैटफ़ॉर्म JAR का इस्तेमाल करने वाले टेस्ट. टारगेट मॉड्यूल, टेस्ट सोर्स वाली Java लाइब्रेरी के कंपैनियन मॉड्यूल की ओर इशारा करता है. R8, कंपैनियन बाइटकोड से रेफ़र की गई सभी क्लास और कोड कॉम्पोनेंट को ट्रेस करता है और उन्हें बनाए रखता है.

तीसरा चरण: मालिकाना हक वाली लाइब्रेरी से नियम एक्सपोर्ट करना

अगर शेयर की गई java_library या android_library को अपने इंटरनल रिफ़्लेक्शन या जेएनआई कॉलबैक के लिए, डेटा को सेव रखने के नियमों की ज़रूरत है, तो लाइब्रेरी टारगेट पर नियम तय करें और 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, उन नियमों को अपने-आप लागू कर देते हैं. इसलिए, डाउनस्ट्रीम ऐप्लिकेशन को उन्हें दोहराने की ज़रूरत नहीं होती. जब अलग-अलग डायरेक्ट्री में मौजूद कई मॉड्यूल को ऐसे नियम सेट को शेयर करना होता है जो किसी एक कोड लाइब्रेरी से नहीं जुड़ा होता है, तो Android.bp में साफ़ तौर पर filegroup के ज़रिए .flags फ़ाइल पब्लिश करें. इससे मॉड्यूल, पैकेज की सीमाओं के हिसाब से :my-shared-flags को आसानी से रेफ़रंस कर सकते हैं. डाउनस्ट्रीम ऐप्लिकेशन के ऑप्टिमाइज़ेशन को सीमित किए बिना, लाइब्रेरी के उपभोक्ता के डेटा को सुरक्षित रखने से जुड़े कीप नियमों को लिखने के बारे में सामान्य दिशा-निर्देशों के लिए, लाइब्रेरी तैयार करने वालों के लिए ऑप्टिमाइज़ेशन लेख पढ़ें.

चौथा चरण: सोर्स कोड में एनोटेट किए गए एलान

जब किसी क्लास, तरीके, कंस्ट्रक्टर या फ़ील्ड को रिफ़्लेक्शन या JNI के ज़रिए ऐक्सेस किया जाता है और वह AAPT2 या ग्लोबल बेसलाइन में शामिल नहीं होता है, तो .flags फ़ाइलों में अलग की गई -keep नियमों को Keep ऐनोटेशन के बारे में गाइड में दिए गए सोर्स ऐनोटेशन से बदलें (keepanno Javadoc रेफ़रंस देखें):

  • @UsesReflection: इस एनोटेशन का इस्तेमाल तब करें, जब आपके पास रिफ़्लेक्शन करने वाले कोड को कंट्रोल करने का विकल्प हो. इसे रिफ़्लेक्शन कॉल साइट पर रखें, ताकि यह एलान किया जा सके कि किन टारगेट क्लास, तरीकों या फ़ील्ड को डाइनैमिक तरीके से ऐक्सेस किया जाता है. @UsesReflection, एनोटेट की गई कॉल साइट के लिए, पहले से मौजूद शर्त को अपने-आप कोड में बदल देता है. इस शर्त के मुताबिक, कॉल साइट तक पहुंचा जा सकता है. इसलिए, अगर कॉल साइट का इस्तेमाल नहीं किया जाता है, तो R8, कॉलर और रिफ़्लेक्टिव टारगेट, दोनों को हटा देता है.
  • @UsedByReflection: इसे उन क्लास, तरीकों, फ़ील्ड या कंस्ट्रक्टर पर रखें जिन्हें बाहरी कोड या लाइब्रेरी से इंस्टैंशिएट या इनवोक किया जाता है. जैसे, Bundle या Settings कुंजियों से Class.forName के साथ लोड की गई क्लास या डाइनैमिक क्लासलोडर में लोड की गई प्लगिन क्लास. kind (जैसे कि KeepItemKind.CLASS_AND_METHODS), preconditions (@KeepCondition), और पैरामीटर की सीमाएं तय करें, ताकि R8 सिर्फ़ सटीक रिफ़्लेक्टिव कॉन्ट्रैक्ट को बनाए रखे. साथ ही, क्लास के इस्तेमाल न किए गए सदस्यों को अब भी ऑप्टिमाइज़ या हटाया जा सके. @UsedByReflection को JobService या BroadcastReceiver जैसे मेनिफ़ेस्ट में रजिस्टर किए गए कॉम्पोनेंट में न जोड़ें, क्योंकि AAPT2 पहले से ही इन्हें सेव रखता है.
  • @UsedByNative: इस एनोटेशन को उन तरीकों या फ़ील्ड पर रखें जिन्हें C या C++ JNI कोड से ऐक्सेस किया जाता है. इसके लिए, GetMethodID, GetStaticMethodID या GetFieldID का इस्तेमाल करें.
  • @KeepForApi: इसे लाइब्रेरी की उन एपीआई क्लास या सदस्यों पर रखें जिन्हें डिस्ट्रिब्यूट करने से पहले, लाइब्रेरी को छोटा करने पर भी बरकरार रखना है.
  • @Keep (androidx.annotation.Keep): इसका इस्तेमाल तब करें, जब keepanno लागू न हो. किसी क्लास पर @Keep लागू करने से, क्लास और उसके सभी कोड कॉम्पोनेंट बिना किसी शर्त के सुरक्षित रहते हैं. किसी तरीके या फ़ील्ड पर @Keep लागू करने से, बिना शर्त एंट्री पॉइंट के तौर पर काम किया जाता है. इससे सदस्य और उसकी क्लास को बनाए रखा जाता है. भले ही, क्लास को कभी इंस्टैंटिएट न किया गया हो. वहीं, keepanno से शर्त के आधार पर पहुंच का पता चलता है.

Soong मॉड्यूल में R8 keepanno एनोटेशन का इस्तेमाल करने के लिए:

  1. Android.bp में "keepanno-annotations" को libs में जोड़ें:

    libs: [
        "keepanno-annotations",
    ],
    
  2. Java या Kotlin सोर्स कोड में, com.android.tools.r8.keepanno.annotations.* इंपोर्ट करें और एलान या कॉल साइट को एनोटेट करें:

    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 नियम जनरेट करता है.

ब्रॉड एक्सएमएल क्लिक हैंडलर के नियमों को बनाए रखना

# Don't do this:
-keepclassmembers class * {
    public void *(android.view.View);
}
  • परफ़ॉर्मेंस पर इसका क्या असर पड़ता है: मॉड्यूल की हर क्लास में हर public void *(View) तरीके को बनाए रखने से, R8 किसी भी ऐसे तरीके को हटाने या इनलाइन करने से रोकता है जो एक View पैरामीटर स्वीकार करता है.
  • सुझाया गया तरीका: नियम को मिटाएं. AAPT2, लेआउट एक्सएमएल फ़ाइलों को स्कैन करता है. साथ ही, android:onClick एट्रिब्यूट से रेफ़र किए गए तरीकों के लिए, टारगेट किए गए कीप नियम जनरेट करता है.

ब्रॉड व्यू के लिए, गेटर और सेटर के रखरखाव से जुड़े नियम

# 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 को ऐप्लिकेशन में मौजूद हर View सबक्लास और लिंक की गई सभी लाइब्रेरी (AndroidX और Material लाइब्रेरी भी शामिल हैं) में हर गेटर और सेटर को बनाए रखना पड़ता है. इससे यूज़र इंटरफ़ेस (यूआई) कोड में, इस्तेमाल नहीं किए जा रहे तरीके हटाने और इनलाइन करने की प्रोसेस रुक जाती है.
  • सुझाया गया तरीका: नियम को मिटाएं. AAPT2, लेआउट एक्सएमएल फ़ाइलों से इन्फ्लेट की गई कस्टम View क्लास के लिए, कंस्ट्रक्टर पहले से ही सेव करता है. अगर कोड, रिफ़्लेक्टिव स्ट्रिंग के नामों का इस्तेमाल करके व्यू प्रॉपर्टी को ऐनिमेट करता है, जैसे कि ObjectAnimator.ofFloat(view, "translationZ", ...), तो स्ट्रिंग के नाम को टाइप की गई प्रॉपर्टी के रेफ़रंस (View.TRANSLATION_Z या कस्टम FloatProperty या IntProperty लागू करने) से बदलें, ताकि रिफ़्लेक्शन से पूरी तरह बचा जा सके. अगर रिफ़्लेक्टिव प्रॉपर्टी के ऐक्सेस से बचा नहीं जा सकता, तो @UsedByReflection का इस्तेमाल करके, खास तौर पर getter या setter को एनोटेट करें.

ब्लैंकेट ऑप्टिमाइज़ेशन या ओब्फ़स्केशन की सुविधा बंद हो जाती है

# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
  • इससे परफ़ॉर्मेंस पर क्या असर पड़ता है: .flags फ़ाइल में -dontoptimize या -dontshrink रखने से, मॉड्यूल की Android.bp सेटिंग अपने-आप बदल जाती हैं. साथ ही, पूरे टारगेट के लिए ऑप्टिमाइज़ेशन पास बंद हो जाते हैं. इससे भी बुरी बात यह है कि अगर कोई लाइब्रेरी, -dontoptimize वाली .flags फ़ाइल एक्सपोर्ट करती है, तो यह लाइब्रेरी से लिंक होने वाले हर डाउनस्ट्रीम android_app के लिए, R8 ऑप्टिमाइज़ेशन को बंद कर देती है.
  • सुझाया गया तरीका: .flags फ़ाइलों से -dontoptimize, -dontshrink, और -dontobfuscate हटाएं. पत्ती वाले टारगेट पर optimize ब्लॉक (shrink, optimize, obfuscate) का इस्तेमाल करके, Android.bp में ऑप्टिमाइज़ेशन के तरीके को साफ़ तौर पर कंट्रोल करें.

कमिट किए गए नियमों में डाइग्नोस्टिक फ़्लैग

# 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 नियम जोड़ने से, कोड को प्रोडक्शन बिल्ड में बिना किसी बदलाव के रखा जाता है. @VisibleForTesting का इस्तेमाल करके तरीकों को एनोटेट करने या trace_references_from को कॉन्फ़िगर करने से, मैन्युअल -keep नियमों को बनाए रखने से बचा जा सकता है. हालांकि, ये सिंबल अब भी प्रोडक्शन बाइनरी में शामिल होते हैं.
  • सुझाया गया तरीका: टेस्ट के लिए बनाए गए एंट्री पॉइंट को प्रोडक्शन APK से पूरी तरह से हटाने के लिए, ऐप्लिकेशन को .impl लाइब्रेरी में स्ट्रक्चर करें. इसके बाद, static_libs का इस्तेमाल करके, इसे खुद से इंस्ट्रुमेंट करने वाले टेस्ट APK में लिंक करें. इसके बारे में दूसरे चरण: टेस्ट-ड्रिवन कीप नियमों को माइग्रेट करना में बताया गया है.

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) या एक्सएमएल (@raw/foo) में पहले से ही स्टैटिक तौर पर रेफ़रंस दिया गया है, तो श्रिंक करने वाला टूल उन्हें अपने-आप बनाए रखता है. इसलिए, आपके पास res/raw/keep.xml को मिटाने का विकल्प होता है.

नियम में किए गए बदलावों की पुष्टि करना और उनकी जांच करना

proguard.flags या keep.xml में डेटा सुरक्षित रखने से जुड़े नियमों को हटाने या कम करने पर, यह पुष्टि करें कि मॉड्यूल सही तरीके से काम कर रहा हो, यूनिट और इंस्ट्रूमेंटेशन टेस्ट पास कर रहा हो, और सभी ज़रूरी एंट्री पॉइंट बनाए रख रहा हो.

मॉड्यूल बनाना और उनकी जांच करना

  1. मॉड्यूल को इस तरह से बनाएं कि R8 और AAPT2, रेफ़रंस से जुड़ी चेतावनियों के बिना पूरा हो जाए:

    m <MODULE_NAME>
    
  2. atest का इस्तेमाल करके, मॉड्यूल के लिए यूनिट और इंस्ट्रुमेंटेशन टेस्ट चलाएं:

    atest <TEST_MODULE_NAME>
    
  3. सिस्टम ऐप्लिकेशन, खास सेवाओं या हार्डवेयर पर निर्भर मॉड्यूल के लिए, टारगेट किए गए फ़िज़िकल डिवाइसों या अपने डिवाइस के टेस्ट लैब में इंस्ट्रुमेंटेशन और यूआई टेस्ट चलाएं. इससे रनटाइम रिफ़्लेक्शन पाथ, आईपीसी बाइंडिंग, और संसाधन इन्फ़्लेशन का इस्तेमाल किया जा सकेगा.

डीईएक्स और संसाधन के अंतर की जांच करना

नियमों में बदलाव करने से पहले और बाद में, कंपाइल किए गए APK या JAR की तुलना करें. इससे यह पुष्टि की जा सकेगी कि R8, डेड कोड और संसाधनों को हटा देता है. हालांकि, वह एंट्री पॉइंट को नहीं हटाता:

  • आउटपुट APK या DEX फ़ाइलों में सेव की गई क्लास, तरीकों, और फ़ील्ड की जांच करने के लिए, apkanalyzer या dexdump का इस्तेमाल करें:

    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
    

R8 की मदद से, कीप रेडियस और नियम के पालन का विश्लेषण करना

ओपन-सोर्स R8 कंपाइलर में, कीप रेडियस ऐनालाइज़र शामिल होता है. इसे Android Studio और 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 को यह निर्देश देता है कि वह out/soong/.intermediates/ के तहत कंपाइल किए गए हर मॉड्यूल के लिए, इंटरमीडिएट r8keepradius.pb फ़ाइल में रिकॉर्ड कीप रूल मेट्रिक रिकॉर्ड करे. हर कीप रूल और keepanno एनोटेशन के लिए, R8 यह जानकारी रिकॉर्ड करता है:

    • तुरंत सुरक्षित रखने का दायरा: नियम के तहत सुरक्षित रखी गई क्लास, फ़ील्ड, और तरीके. साथ ही, कोड को छोटा करने, ऑप्टिमाइज़ करने या छिपाने के ख़िलाफ़ लागू की गई खास पाबंदियां.
    • नियमों का पालन करना: कौनसे अन्य कीप रूल या एनोटेशन पहले से ही एक जैसे आइटम सेव करते हैं. अगर कोई कस्टम नियम, AAPT2 के नियम या ग्लोबल बेसलाइन नियम के दायरे में पूरी तरह से आता है, तो उसे सुरक्षित तरीके से मिटाया जा सकता है.
    • पूरे पैकेज और ग्लोबल लेवल पर लागू होने वाले नियम: ऐसे नियम जो पूरे पैकेज के लिए वाइल्डकार्ड का इस्तेमाल करते हैं या ग्लोबल कॉन्फ़िगरेशन डायरेक्टिव लागू करते हैं.
  2. r8keepradius.pb आउटपुट को इंटरैक्टिव एचटीएमएल रिपोर्ट में बदलें. इसके लिए, 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 फ़ाइलों को स्कैन करता है. साथ ही, हर मॉड्यूल के लिए एक एचटीएमएल रिपोर्ट जनरेट करता है. इसके अलावा, यह out/keep_radius_reports/keepradius.html बनाता है. इसमें लाइव आइटम की संख्या, रखे गए आइटम की संख्या, और बिल्ड में सबसे ज़्यादा रेडियस वाले कीप नियमों की खास जानकारी होती है. जनरेट की गई रिपोर्ट में, कोड के साइज़ को कम करने, ऑप्टिमाइज़ करने, और कोड को छिपाने से जुड़े स्कोर को समझने के बारे में ज़्यादा जानने के लिए, R8 कॉन्फ़िगरेशन ऐनलिसिस टूल का इस्तेमाल करना लेख पढ़ें.

Soong, build/soong/java/dex.go में R8 के दो अन्य डाइग्नोस्टिक एनवायरमेंट वैरिएबल का भी इस्तेमाल करता है:

  • R8_DUMP_INPUT=true: यह r8inputs.zip को मॉड्यूल की इंटरमीडिएट डायरेक्ट्री में लिखता है. इस डायरेक्ट्री में सभी इनपुट JAR, लाइब्रेरी JAR, और मर्ज किए गए ProGuard कॉन्फ़िगरेशन होते हैं. इनका इस्तेमाल, स्टैंडअलोन R8 को फिर से बनाने के लिए किया जाता है.
  • R8_DUMP_PERFETTO_TRACE=true: यह r8trace.ptrace को मॉड्यूल की इंटरमीडिएट डायरेक्ट्री में लिखता है, ताकि Perfetto में R8 कंपाइलेशन पास की जांच की जा सके.

लाइब्रेरी के उपभोक्ता नियमों की पुष्टि करना

जब कोई 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 का इस्तेमाल करके, किसी बिल्ड में मौजूद हर नियम के क्लास, फ़ील्ड, और तरीके के रिटेंशन रेडियस का सटीक आकलन कर सकती हैं.