'रखें' नियम जोड़ना

किसी क्लास (या सबक्लास या लागू करने का तरीका) और उस क्लास में मौजूद तरीकों, कंस्ट्रक्टर या फ़ील्ड जैसे कोड कॉम्पोनेंट को ऑप्टिमाइज़ेशन की प्रोसेस से सुरक्षित रखने के लिए, कीप रूल तय किया जाता है.

कीप रूल का सामान्य सिंटैक्स इस तरह होता है:


-<keep_option>[,<keep_option_modifier_1>,<keep_option_modifier_2>,...] <class_specification>

यहां, कीप रूल का उदाहरण दिया गया है. इसमें keepclassmembers को सुरक्षित रखने के विकल्प के तौर पर, allowoptimization को मॉडिफ़ायर के तौर पर इस्तेमाल किया गया है. साथ ही, com.example.MyClass से someSpecificMethod() को सुरक्षित रखा गया है:

-keepclassmembers,allowoptimization class com.example.MyClass {
  void someSpecificMethod();
}

कीप ऑप्शन

कीप ऑप्शन, कीप रूल का पहला हिस्सा होता है. इससे यह तय किया जाता है कि कोड ऑप्टिमाइज़ेशन के दौरान क्लास के कौन-कौनसे कोड कॉम्पोनेंट को सुरक्षित रखना है. कीप रूल में छह अलग-अलग विकल्प होते हैं. जैसे, keep, keepclassmembers, keepclasseswithmembers, keepnames, keepclassmembernames, keepclasseswithmembernames.

यहां दी गई टेबल में, कीप रूल के इन विकल्पों के बारे में बताया गया है:

कीप ऑप्शन जानकारी
keepclassmembers यह विकल्प, क्लास में तय किए गए कोड कॉम्पोनेंट को सिर्फ़ तभी सुरक्षित रखता है, जब ऑप्टिमाइज़ेशन के बाद भी क्लास मौजूद हो.
keep यह विकल्प, तय की गई क्लास के साथ-साथ उसके फ़ील्ड और तरीकों को भी सुरक्षित रखता है. इससे उन्हें ऑप्टिमाइज़ होने से रोका जाता है.

ध्यान दें: आम तौर पर, keep का इस्तेमाल सिर्फ़ कीप ऑप्शन मॉडिफ़ायर के साथ किया जाना चाहिए. क्योंकि, keep का इस्तेमाल करने पर, मैच की गई क्लास के लिए किसी भी तरह का ऑप्टिमाइज़ेशन नहीं किया जा सकता.
keepclasseswithmembers यह विकल्प, किसी क्लास में तय किए गए कोड कॉम्पोनेंट को सिर्फ़ तभी सुरक्षित रखता है, जब क्लास में उसके सभी कोड कॉम्पोनेंट शामिल हों.
keepclassmembernames इससे क्लास में शामिल कोड कॉम्पोनेंट के नाम बदलने पर रोक लग जाती है. हालांकि, इससे क्लास या उसमें शामिल कोड कॉम्पोनेंट को हटाने पर कोई असर नहीं पड़ता.

ध्यान दें: इस विकल्प का मतलब अक्सर गलत समझा जाता है. इसलिए, इसके बजाय ठीक वैसा ही -keepclassmembers,allowshrinking इस्तेमाल करें.
keepnames इससे क्लास और उसके कोड कॉम्पोनेंट के नाम बदलने से रोका जाता है. हालांकि, अगर क्लास का इस्तेमाल नहीं किया जा रहा है, तो उसे पूरी तरह से हटाने से नहीं रोका जाता.

ध्यान दें: इस विकल्प का मतलब अक्सर गलत समझा जाता है. इसलिए, इसके बजाय ठीक वैसा ही -keep,allowshrinking इस्तेमाल करें.
keepclasseswithmembernames इससे क्लास और उसके तय किए गए कोड कॉम्पोनेंट के नाम बदलने से रोका जाता है. हालांकि, ऐसा सिर्फ़ तब किया जा सकता है, जब कोड कॉम्पोनेंट फ़ाइनल कोड में मौजूद हों. इससे कोड को हटाने से नहीं रोका जा सकता.

ध्यान दें: इस विकल्प का मतलब अक्सर गलत समझा जाता है. इसलिए, इसके बजाय ठीक वैसा ही -keepclasseswithmembers,allowshrinking इस्तेमाल करें.

सही कीप ऑप्शन चुनना

अपने ऐप्लिकेशन के लिए सही ऑप्टिमाइज़ेशन तय करने के लिए, सही कीप ऑप्शन चुनना अहम है. कुछ कीप ऑप्शन कोड को छोटा कर देते हैं. इस प्रोसेस में, बिना रेफ़रंस वाले कोड को हटा दिया जाता है. वहीं, अन्य विकल्पों से कोड को छिपा दिया जाता है या उसका नाम बदल दिया जाता है. यहां दी गई टेबल में, अलग-अलग कीप विकल्पों में की जाने वाली कार्रवाइयों के बारे में बताया गया है:

कीप ऑप्शन ऐप्लिकेशन में इस्तेमाल न होने वाली क्लास को हटा देता है ऐप्लिकेशन की क्लास के नाम बदल कर उन्हें छिपाता है क्लास में मौजूद कोड कॉम्पोनेंट की संख्या कम करता है कोड कॉम्पोनेंट के नाम बदल कर उन्हें छिपाता है
keep
keepclassmembers
keepclasseswithmembers
keepnames
keepclassmembernames
keepclasseswithmembernames

कीप ऑप्शन मॉडिफ़ायर

कीप रूल के दायरे और काम करने के तरीके को कंट्रोल करने के लिए, कीप ऑप्शन मॉडिफ़ायर का इस्तेमाल किया जाता है. कीप रूल में, 0 या उससे ज़्यादा कीप ऑप्शन मॉडिफ़ायर जोड़े जा सकते हैं.

कीप ऑप्शन मॉडिफ़ायर की वैल्यू के बारे में यहां दी गई टेबल में बताया गया है:

वैल्यू जानकारी
allowoptimization इससे, तय किए गए एलिमेंट को ऑप्टिमाइज़ करने की अनुमति मिलती है. हालांकि, बताए गए एलिमेंट का नाम नहीं बदला जाता या उन्हें हटाया नहीं जाता.
allowobfucastion इससे, चुने गए एलिमेंट का नाम बदला जा सकता है. हालांकि, एलिमेंट को हटाया नहीं जाता और न ही उन्हें ऑप्टिमाइज़ किया जाता है.
allowshrinking अगर R8 को, बताए गए एलिमेंट के कोई रेफ़रंस नहीं मिलते हैं, तो उन्हें हटाने की अनुमति देता है. हालांकि, एलिमेंट का नाम नहीं बदला जाता है और न ही उन्हें ऑप्टिमाइज़ किया जाता है.
includedescriptorclasses यह R8 को उन सभी क्लास को बनाए रखने का निर्देश देता है जो सुरक्षित रखी जा रही क्लास के तरीकों (पैरामीटर टाइप और रिटर्न टाइप) और फ़ील्ड (फ़ील्ड टाइप) के डिस्क्रिप्टर में दिखती हैं.
allowaccessmodification इस विकल्प की मदद से, R8 को ऑप्टिमाइज़ेशन प्रोसेस के दौरान क्लास, तरीकों, और फ़ील्ड के ऐक्सेस मॉडिफ़ायर (public, private, protected) को बदलने (आम तौर पर बढ़ाने) की अनुमति मिलती है.
allowrepackage इस विकल्प की मदद से, R8 क्लास को अलग-अलग पैकेज में ले जा सकता है. इनमें डिफ़ॉल्ट (रूट) पैकेज भी शामिल है.

क्लास की जानकारी

आपको हर कीप रूल में एक क्लास (इसमें इंटरफ़ेस, एनम, और एनोटेशन क्लास शामिल हैं) तय करनी होगी. एनोटेशन के आधार पर, नियम को सीमित किया जा सकता है. इसके लिए, सुपरक्लास या लागू किया गया इंटरफ़ेस तय करें या क्लास के लिए ऐक्सेस मॉडिफ़ायर तय करें. सभी क्लास, जिनमें java.lang नेमस्पेस की क्लास जैसे कि java.lang.String शामिल हैं, को उनके पूरी तरह क्वालिफ़ाइड Java नाम का इस्तेमाल करके तय किया जाना चाहिए. इस्तेमाल किए जाने वाले नामों को समझने के लिए, जनरेट किए गए Java नामों की जांच करना में बताए गए टूल का इस्तेमाल करके, बाइटकोड की जांच करें.

यहां दिए गए उदाहरण में बताया गया है कि आपको MaterialButton क्लास कैसे तय करनी चाहिए:

  • सही: com.google.android.material.button.MaterialButton
  • गलत: MaterialButton

क्लास स्पेसिफ़िकेशन में, क्लास के उन कोड कॉम्पोनेंट के बारे में भी जानकारी दी जाती है जिन्हें सुरक्षित रखना है. उदाहरण के लिए, यहां दिया गया नियम MyClass क्लास और someSpecificMethod() तरीके को सुरक्षित रखता है:

-keep class com.example.MyClass {
  void someSpecificMethod();
}

एनोटेशन के आधार पर क्लास तय करना

एनोटेशन के आधार पर क्लास तय करने के लिए, एनोटेशन के पूरी तरह से क्वालिफ़ाइड किए गए Java नाम के आगे @ सिंबल लगाएं. उदाहरण के लिए:

-keep class @com.example.MyAnnotation com.example.MyClass

अगर किसी कीप रूल में एक से ज़्यादा एनोटेशन हैं, तो यह उन क्लास को सेव करता है जिनमें सूची में दिए गए सभी एनोटेशन मौजूद हैं. एक से ज़्यादा एनोटेशन लिस्ट किए जा सकते हैं. हालांकि, यह नियम सिर्फ़ तब लागू होता है, जब क्लास में लिस्ट किया गया हर एनोटेशन मौजूद हो. उदाहरण के लिए, यहां दिया गया नियम, Annotation1 और Annotation2, दोनों से एनोटेट की गई सभी क्लास को बनाए रखता है.

-keep class @com.example.Annotation1 @com.example.Annotation2 *

सबक्लास और लागू करने के तरीके के बारे में जानकारी देना

किसी सबक्लास या इंटरफ़ेस को लागू करने वाली क्लास को टारगेट करने के लिए, extend और implements का इस्तेमाल करें.

उदाहरण के लिए, अगर आपके पास Bar क्लास है और Foo सबक्लास है, तो:

class Foo : Bar()

यहां दिया गया कीप रूल, Bar की सभी सबक्लास को सुरक्षित रखता है. ध्यान दें कि कीप रूल में, सुपरक्लास Bar को शामिल नहीं किया जाता.

-keep class * extends Bar

अगर आपके पास Foo क्लास है, जो Bar इंटरफ़ेस को लागू करती है, तो:

class Foo : Bar

नीचे दिए गए कीप रूल से, Bar को लागू करने वाली सभी क्लास को सुरक्षित रखा जाता है. ध्यान दें कि कीप रूल में, इंटरफ़ेस Bar शामिल नहीं है.

-keep class * implements Bar

ऐक्सेस मॉडिफ़ायर के आधार पर क्लास तय करना

अपने कीप रूल को ज़्यादा सटीक बनाने के लिए, ऐक्सेस मॉडिफ़ायर तय किए जा सकते हैं. जैसे, public, private, static, और final.

उदाहरण के लिए, यहां दिया गया नियम, api पैकेज और उसके सब-पैकेज में मौजूद सभी public क्लास और इन क्लास में मौजूद सभी सार्वजनिक और सुरक्षित कोड कॉम्पोनेंट को बनाए रखता है.

-keep public class com.example.api.** { public protected *; }

किसी क्लास में शामिल कोड कॉम्पोनेंट के लिए भी मॉडिफ़ायर इस्तेमाल किए जा सकते हैं. उदाहरण के लिए, यहां दिया गया नियम, Utils क्लास के सिर्फ़ public static तरीकों को बनाए रखता है:

-keep class com.example.Utils {
  public static void *(...);
}

Kotlin के लिए खास तौर पर बनाए गए मॉडिफ़ायर

R8, Kotlin के खास मॉडिफ़ायर के साथ काम नहीं करता. जैसे, internal और suspend. ऐसे फ़ील्ड को बनाए रखने के लिए, इन दिशा-निर्देशों का पालन करें.

  • किसी internal क्लास, तरीके या फ़ील्ड को बनाए रखने के लिए, उसे सार्वजनिक के तौर पर ट्रीट करें. उदाहरण के लिए, यहां दिया गया Kotlin सोर्स देखें:

    package com.example
    internal class ImportantInternalClass {
      internal val f: Int
      internal fun m() {}
    }
    

    Kotlin कंपाइलर से जनरेट हुई .class फ़ाइलों में, internal क्लास, तरीके, और फ़ील्ड public होते हैं. इसलिए, आपको public कीवर्ड का इस्तेमाल करना होगा. जैसा कि इस उदाहरण में दिखाया गया है:

    -keepclassmembers public class com.example.ImportantInternalClass {
      public int f;
      public void m();
    }
    
  • जब किसी suspend कोड कॉम्पोनेंट को कंपाइल किया जाता है, तो कंपाइल किए गए उसके सिग्नेचर का मिलान, कीप रूल में मौजूद सिग्नेचर से करें.

    उदाहरण के लिए, अगर आपने fetchUser फ़ंक्शन को इस स्निपेट में दिखाए गए तरीके से तय किया है:

    suspend fun fetchUser(id: String): User
    

    कंपाइल करने पर, बाइटकोड में इसका सिग्नेचर इस तरह दिखता है:

    public final Object fetchUser(String id, Continuation<? super User> continuation);
    

    इस फ़ंक्शन के लिए निजी डेटा के रखरखाव का नियम लिखने के लिए, आपको कंपाइल किए गए इस सिग्नेचर से मिलान करना होगा या ... का इस्तेमाल करना होगा.

    कंपाइल किए गए सिग्नेचर का इस्तेमाल करने का उदाहरण यहां दिया गया है:

    -keepclassmembers class com.example.repository.UserRepository {
      public java.lang.Object fetchUser(java.lang.String,  kotlin.coroutines.Continuation);
    }
    

    ... का इस्तेमाल करने का एक उदाहरण यहां दिया गया है:

    -keepclassmembers class com.example.repository.UserRepository {
      public java.lang.Object fetchUser(...);
    }
    

कोड कॉम्पोनेंट की जानकारी

क्लास स्पेसिफ़िकेशन में, क्लास के उन कोड कॉम्पोनेंट को शामिल किया जा सकता है जिन्हें सुरक्षित रखना है. अगर किसी क्लास के लिए एक या उससे ज़्यादा कोड कॉम्पोनेंट को चुना जाता है, तो यह नियम अन्य कोड कॉम्पोनेंट पर लागू नहीं होता.

एनोटेशन के आधार पर कोड कॉम्पोनेंट के बारे में जानकारी देना

एनोटेशन के आधार पर कोड कॉम्पोनेंट तय किए जा सकते हैं. क्लास की तरह ही, एनोटेशन के पूरी तरह क्वालिफ़ाइड Java नाम के आगे @ प्रीफ़िक्स लगाएं. इसकी मदद से, किसी क्लास में सिर्फ़ उन कोड कॉम्पोनेंट को रखा जा सकता है जिन्हें खास एनोटेशन के साथ मार्क किया गया है. उदाहरण के लिए, @com.example.MyAnnotation के साथ एनोटेट किए गए तरीकों और फ़ील्ड को बनाए रखने के लिए:

-keep class com.example.MyClass {
  @com.example.MyAnnotation <methods>;
  @com.example.MyAnnotation <fields>;
}

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

-keep class @com.example.ClassAnnotation * {
  @com.example.MethodAnnotation <methods>;
  @com.example.FieldAnnotation <fields>;
}

इससे क्लास को @ClassAnnotation के साथ एनोटेट किया जाता है. साथ ही, उन क्लास में, @MethodAnnotation के साथ एनोटेट किए गए तरीकों और @FieldAnnotation के साथ एनोटेट किए गए फ़ील्ड को बनाए रखा जाता है.

जहां तक हो सके, एनोटेशन के आधार पर डेटा बनाए रखने के नियमों का इस्तेमाल करें. इस तरीके से, आपके कोड और कीप रूल के बीच एक साफ़ लिंक मिलता है. साथ ही, इससे अक्सर ज़्यादा मज़बूत कॉन्फ़िगरेशन मिलते हैं. उदाहरण के लिए, androidx.annotation एनोटेशन लाइब्रेरी इस तरीके का इस्तेमाल करती है.

तरीके

किसी कीप रूल के लिए, कोड कॉम्पोनेंट की खास जानकारी में किसी तरीके को तय करने का सिंटैक्स इस तरह होता है:

[<access_modifier>] [<return_type>] <method_name>(<parameter_types>);

उदाहरण के लिए, नीचे दिया गया कीप रूल, setLabel() नाम के एक सार्वजनिक तरीके को सुरक्षित रखता है. यह तरीका, कोई वैल्यू नहीं दिखाता और String लेता है.

-keep class com.example.MyView {
    public void setLabel(java.lang.String);
}

किसी क्लास में मौजूद सभी तरीकों को मैच करने के लिए, <methods> को शॉर्टकट के तौर पर इस्तेमाल किया जा सकता है. इसके लिए, यह तरीका अपनाएं:

-keep class com.example.MyView {
    <methods>;
}

रिटर्न टाइप और पैरामीटर टाइप के लिए टाइप तय करने के बारे में ज़्यादा जानने के लिए, टाइप देखें.

कंस्ट्रक्टर

कंस्ट्रक्टर के बारे में बताने के लिए, <init> का इस्तेमाल करें. निजी डेटा के रखरखाव के नियम के लिए, कोड कॉम्पोनेंट की खास जानकारी में कंस्ट्रक्टर तय करने का सिंटैक्स इस तरह होता है:

[<access_modifier>] <init>(parameter_types);

उदाहरण के लिए, यहां दिया गया कीप रूल, कस्टम View कंस्ट्रक्टर को सेव रखता है. यह Context और AttributeSet लेता है.

-keep class com.example.ui.MyCustomView {
    public <init>(android.content.Context, android.util.AttributeSet);
}

सभी सार्वजनिक कंस्ट्रक्टर को बनाए रखने के लिए, इस उदाहरण का रेफ़रंस दें:

-keep class com.example.ui.MyCustomView {
    public <init>(...);
}

फ़ील्ड

किसी कीप रूल के लिए कोड कॉम्पोनेंट की खास जानकारी में फ़ील्ड तय करने का सिंटैक्स यहां दिया गया है:

[<access_modifier>...] [<type>] <field_name>;

उदाहरण के लिए, यहां दिया गया कीप रूल, userId नाम के एक प्राइवेट स्ट्रिंग फ़ील्ड और STATUS_ACTIVE नाम के एक पब्लिक स्टैटिक इंटिजर फ़ील्ड को सुरक्षित रखता है:

-keep class com.example.models.User {
    private java.lang.String userId;
    public static int STATUS_ACTIVE;
}

किसी क्लास के सभी फ़ील्ड को मैच करने के लिए, <fields> को शॉर्टकट के तौर पर इस तरह इस्तेमाल किया जा सकता है:

-keep class com.example.models.User {
    <fields>;
}

टाइप

इस सेक्शन में, कीप रूल के कोड कॉम्पोनेंट के स्पेसिफ़िकेशन में, रिटर्न टाइप, पैरामीटर टाइप, और फ़ील्ड टाइप तय करने का तरीका बताया गया है. अगर टाइप, Kotlin सोर्स कोड से अलग हैं, तो टाइप तय करने के लिए जनरेट किए गए Java नामों का इस्तेमाल करना न भूलें.

प्रिमिटिव टाइप

प्रिमिटिव टाइप तय करने के लिए, उसके Java कीवर्ड का इस्तेमाल करें. R8 इन प्रिमिटिव टाइप को पहचानता है: boolean, byte, short, char, int, long, float, double.

प्रिमिटिव टाइप वाले नियम का उदाहरण यहां दिया गया है:

# Keeps a method that takes an int and a float as parameters.
-keepclassmembers class com.example.Calculator {
    public void setValues(int, float);
}

जेनरिक टाइप

कंपाइलेशन के दौरान, Kotlin/Java कंपाइलर सामान्य टाइप की जानकारी मिटा देता है. इसलिए, जेनरिक टाइप वाले कीप रूल लिखते समय, आपको अपने कोड के कंपाइल किए गए वर्शन को टारगेट करना होगा, न कि ओरिजनल सोर्स कोड को. जेनरिक टाइप में बदलाव करने के तरीके के बारे में ज़्यादा जानने के लिए, टाइप इरेज़र देखें.

उदाहरण के लिए, अगर आपके पास Box.kt में तय किए गए अनबाउंडेड जेनरिक टाइप वाला यह कोड है:

package com.myapp.data

class Box<T>(val item: T) {
    fun getItem(): T {
        return item
    }
}

टाइप इरेज़र के बाद, T को Object से बदल दिया जाता है. क्लास कंस्ट्रक्टर और तरीके को सुरक्षित रखने के लिए, आपके नियम में सामान्य T के बजाय java.lang.Object का इस्तेमाल किया जाना चाहिए.

कीप रूल का एक उदाहरण यहां दिया गया है:

# Keep the constructor and methods of the Box class.
-keep class com.myapp.data.Box {
    public init(java.lang.Object);
    public java.lang.Object getItem();
}

अगर आपके पास NumberBox.kt में बाउंडेड जेनरिक टाइप वाला यह कोड है:

package com.myapp.data

// T is constrained to be a subtype of Number
class NumberBox<T : Number>(val number: T)

इस मामले में, टाइप इरेज़र T को उसके बाउंड java.lang.Number से बदल देता है.

कीप रूल का एक उदाहरण यहां दिया गया है:

-keep class com.myapp.data.NumberBox {
    public init(java.lang.Number);
}

ऐप्लिकेशन के हिसाब से जेनरिक टाइप को बेस क्लास के तौर पर इस्तेमाल करते समय, बेस क्लास के लिए भी कीप रूल को शामिल करना ज़रूरी है.

उदाहरण के लिए, इस कोड के लिए:

package com.myapp.data

data class UnpackOptions(val useHighPriority: Boolean)

// The generic Box class with UnpackOptions as the bounded type
class Box<T: UnpackOptions>(val item: T) {
}

includedescriptorclasses के साथ कीप रूल का इस्तेमाल करके, UnpackOptions क्लास और Box क्लास के तरीके, दोनों को एक ही नियम के साथ सुरक्षित रखा जा सकता है. इसके लिए, यहां दिया गया तरीका अपनाएं:

-keep,includedescriptorclasses class com.myapp.data.Box {
    public <init>(com.myapp.data.UnpackOptions);
}

अगर आपको ऑब्जेक्ट की सूची को प्रोसेस करने वाले किसी फ़ंक्शन को बनाए रखना है, तो एक ऐसा नियम लिखना होगा जो फ़ंक्शन के सिग्नेचर से पूरी तरह मेल खाता हो. ध्यान दें कि जेनरिक टाइप मिटा दिए जाते हैं. इसलिए, List<Product> जैसे पैरामीटर को java.util.List के तौर पर देखा जाता है.

उदाहरण के लिए, अगर आपके पास एक यूटिलिटी क्लास है, जिसमें एक ऐसा फ़ंक्शन है जो Product ऑब्जेक्ट की सूची को इस तरह प्रोसेस करता है:

package com.myapp.utils

import com.myapp.data.Product
import android.util.Log

class DataProcessor {
    // This is the function we want to keep
    fun processProducts(products: List<Product>) {
        Log.d("DataProcessor", "Processing ${products.size} products.")
        // Business logic ...
    }
}

// The data class used in the list (from the previous example)
package com.myapp.data
data class Product(val id: String, val name: String)

सिर्फ़ processProductsफ़ंक्शन को सुरक्षित रखने के लिए, इस कीप रूल का इस्तेमाल किया जा सकता है:

-keep class com.myapp.utils.DataProcessor {
    public void processProducts(java.util.List);
}

ऐरे टाइप

ऐरे के हर डाइमेंशन के लिए, कॉम्पोनेंट टाइप में [] जोड़कर ऐरे का टाइप तय करें. यह क्लास टाइप और प्रिमिटिव टाइप, दोनों पर लागू होता है.

  • एक डाइमेंशन वाला क्लास ऐरे: java.lang.String[]
  • दो डाइमेंशन वाला प्रिमिटिव ऐरे: int[][]

उदाहरण के लिए, अगर आपके पास यह कोड है:

package com.example.data

class ImageProcessor {
  fun process(): ByteArray {
    // process image to return a byte array
  }
}

यहां दिया गया कीप रूल इस्तेमाल किया जा सकता है:

# Keeps a method that returns a byte array.
-keepclassmembers class com.example.data.ImageProcessor {
    public byte[] process();
}

उदाहरण

उदाहरण के लिए, किसी क्लास और उसके सभी कोड कॉम्पोनेंट को सुरक्षित रखने के लिए, इसका इस्तेमाल करें:

-keep class com.myapp.MyClass { *; }

सिर्फ़ क्लास और उसके डिफ़ॉल्ट कंस्ट्रक्टर को सुरक्षित रखने के लिए, लेकिन अन्य कोड कॉम्पोनेंट को नहीं, तो इसका इस्तेमाल करें:

-keep class com.myapp.MyClass

हमारा सुझाव है कि आप हमेशा कुछ कोड कॉम्पोनेंट के बारे में बताएं. जैसे, यहां दिए गए उदाहरण में क्लास MyClass में, पब्लिक फ़ील्ड text और पब्लिक मेथड updateText() को रखा गया है.

-keep class com.myapp.MyClass {
    public java.lang.String text;
    public void updateText(java.lang.String);
}

सभी सार्वजनिक फ़ील्ड और सार्वजनिक तरीके सुरक्षित रखने के लिए, यह उदाहरण देखें:

-keep public class com.example.api.ApiClient {
    public *;
}

कोड कॉम्पोनेंट की जानकारी शामिल न करना

कोड कॉम्पोनेंट के स्पेसिफ़िकेशन को हटाने पर, R8 क्लास के लिए डिफ़ॉल्ट कंस्ट्रक्टर को बनाए रखता है.

उदाहरण के लिए, अगर आपने -keep class com.example.MyClass या -keep class com.example.MyClass {} लिखा है, तो R8 इन्हें इस तरह से ट्रीट करेगा:

-keep class com.example.MyClass{
  void <init>();
}

पैकेज-लेवल के फ़ंक्शन

अगर आपको किसी क्लास के बाहर (आम तौर पर, इन्हें टॉप लेवल फ़ंक्शन कहा जाता है) तय किए गए Kotlin फ़ंक्शन का रेफ़रंस देना है, तो पक्का करें कि आपने Kotlin कंपाइलर की ओर से अपने-आप जोड़ी गई क्लास के लिए, जनरेट किए गए Java नाम का इस्तेमाल किया हो. क्लास का नाम, Kotlin फ़ाइल का नाम होता है. इसमें Kt जोड़ा जाता है. उदाहरण के लिए, अगर आपके पास MyClass.kt नाम की Kotlin फ़ाइल है, जिसे इस तरह से तय किया गया है:

package com.example.myapp.utils

// A top-level function not inside a class
fun isEmailValid(email: String): Boolean {
    return email.contains("@")
}

isEmailValid फ़ंक्शन के लिए, डेटा के रखरखाव का नियम लिखने के लिए, क्लास स्पेसिफ़िकेशन को जनरेट की गई क्लास MyClassKt को टारगेट करना होगा:

-keep class com.example.myapp.utils.MyClassKt {
    public static boolean isEmailValid(java.lang.String);
}

वाइल्डकार्ड

यहां दी गई टेबल में बताया गया है कि वाइल्डकार्ड का इस्तेमाल करके, एक जैसे पैटर्न वाली कई क्लास या कोड कॉम्पोनेंट पर, सुरक्षित रखने के नियम कैसे लागू किए जाते हैं.

वाइल्डकार्ड क्लास या कोड कॉम्पोनेंट पर लागू होता है जानकारी
** दोनों इसका इस्तेमाल सबसे ज़्यादा किया जाता है. यह किसी भी टाइप के नाम से मेल खाता है. इसमें पैकेज सेपरेटर की कोई भी संख्या शामिल हो सकती है. यह किसी पैकेज और उसके सब-पैकेज में मौजूद सभी क्लास का मिलान करने के काम आता है.
* दोनों क्लास स्पेसिफ़िकेशन के लिए, यह टाइप के नाम के किसी भी ऐसे हिस्से से मेल खाता है जिसमें पैकेज सेपरेटर (.) शामिल नहीं होते हैं
कोड कॉम्पोनेंट स्पेसिफ़िकेशन के लिए, यह किसी भी तरीके या फ़ील्ड के नाम से मेल खाता है. इसका इस्तेमाल अकेले करने पर, यह ** का उपनाम भी होता है.
? दोनों क्लास या कोड कॉम्पोनेंट के नाम में मौजूद किसी भी वर्ण से मेल खाता है.
*** कोड कॉम्पोनेंट यह किसी भी टाइप से मेल खाता है. जैसे, प्रिमिटिव टाइप (जैसे, int), क्लास टाइप (जैसे, java.lang.String), और किसी भी डाइमेंशन के ऐरे टाइप (जैसे, byte[][]).
... कोड कॉम्पोनेंट किसी तरीके के लिए पैरामीटर की किसी भी सूची से मेल खाता है.
% कोड कॉम्पोनेंट किसी भी प्रिमिटिव टाइप (जैसे कि `int`, `float`, `boolean` या अन्य) से मेल खाता है.

खास वाइल्डकार्ड इस्तेमाल करने के कुछ उदाहरण यहां दिए गए हैं:

  • अगर आपके पास एक ही नाम वाले कई ऐसे तरीके हैं जो अलग-अलग प्रिमिटिव टाइप को इनपुट के तौर पर लेते हैं, तो % का इस्तेमाल करके, एक ऐसा नियम लिखा जा सकता है जो उन सभी को सुरक्षित रखता है. उदाहरण के लिए, इस DataStore क्लास में कई setValue तरीके हैं:

    class DataStore {
        fun setValue(key: String, value: Int) { ... }
        fun setValue(key: String, value: Boolean) { ... }
        fun setValue(key: String, value: Float) { ... }
    }
    

    कीप रूल के तहत, सभी तरीकों को सुरक्षित रखा जाता है:

    -keep class com.example.DataStore {
        public void setValue(java.lang.String, %);
    }
    
  • अगर आपके पास ऐसी कई क्लास हैं जिनके नाम में सिर्फ़ एक वर्ण का अंतर है, तो उन सभी को सुरक्षित रखने के लिए, ? का इस्तेमाल करके एक नियम लिखें. उदाहरण के लिए, अगर आपके पास ये क्लास हैं:

    com.example.models.UserV1 {...}
    com.example.models.UserV2 {...}
    com.example.models.UserV3 {...}
    

    यहां दिया गया कीप रूल, सभी क्लास को सुरक्षित रखता है:

    -keep class com.example.models.UserV?
    
  • अगर आपको Example और AnotherExample क्लास (अगर वे रूट-लेवल की क्लास थीं) को मैच करना है, लेकिन com.foo.Example को नहीं, तो इस कीप रूल का इस्तेमाल करें:

    -keep class *Example
    
  • सिर्फ़ * का इस्तेमाल करने पर, यह ** के लिए उपनाम के तौर पर काम करता है. उदाहरण के लिए, यहां दिए गए कीप रूल एक जैसे हैं:

    -keepclasseswithmembers class * { public static void main(java.lang.String[];) }
    
    -keepclasseswithmembers class ** { public static void main(java.lang.String[];) }
    

जनरेट किए गए Java नामों की जांच करना

कीप रूल लिखते समय, आपको क्लास और अन्य रेफ़रंस टाइप के नाम बताने होंगे. ऐसा तब करना होगा, जब उन्हें Java बाइटकोड में कंपाइल कर लिया गया हो. उदाहरण के लिए, क्लास स्पेसिफ़िकेशन और टाइप देखें. अपने कोड के लिए जनरेट किए गए Java नाम देखने के लिए, Android Studio में इनमें से किसी एक टूल का इस्तेमाल करें:

  • APK ऐनालाइज़र
  • Kotlin सोर्स फ़ाइल खुली होने पर, बाइटकोड की जांच करें. इसके लिए, Tools > Kotlin > Show Kotlin Bytecode > Decompile पर जाएं.