রাখার নিয়ম যোগ করুন

উচ্চ স্তরে, একটি 'কিপ রুল' একটি ক্লাস (বা সাবক্লাস বা ইমপ্লিমেন্টেশন) এবং তারপর সেই ক্লাসের অন্তর্ভুক্ত মেম্বার—যেমন মেথড, কনস্ট্রাক্টর বা ফিল্ড—সংরক্ষণের জন্য নির্দিষ্ট করে।

একটি কিপ রুলের সাধারণ সিনট্যাক্সটি নিম্নরূপ:


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

নিম্নলিখিতটি একটি keep নিয়মের উদাহরণ যা keep বিকল্প হিসাবে keepclassmembers , মডিফায়ার হিসাবে allowoptimization ব্যবহার করে এবং com.example.MyClass থেকে someSpecificMethod() কে রাখে:

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

বিকল্পটি রাখুন

keep অপশনটি আপনার keep রুলের প্রথম অংশ। এটি নির্দিষ্ট করে যে একটি ক্লাসের কোন দিকগুলো সংরক্ষণ করতে হবে। ছয়টি ভিন্ন keep অপশন রয়েছে, যথা keep , keepclassmembers , keepclasseswithmembers , keepnames , keepclassmembernames , keepclasseswithmembernames

নিম্নলিখিত সারণিতে এই রাখার বিকল্পগুলি বর্ণনা করা হয়েছে:

বিকল্পটি রাখুন বর্ণনা
keepclassmembers নির্দিষ্ট মেম্বারগুলোকে কেবল তখনই সংরক্ষণ করা হয়, যদি R8 সেগুলোকে ধারণকারী ক্লাসটি অপসারণ না করে।
keep নির্দিষ্ট ক্লাস এবং সেগুলোর মেম্বারদের (ফিল্ড ও মেথড) সংরক্ষণ করে, ফলে সেগুলোকে অপ্টিমাইজ করা যায় না।

দ্রষ্টব্য : keep সাধারণত শুধুমাত্র 'keep' অপশন মডিফায়ারের সাথেই ব্যবহার করা উচিত, কারণ শুধুমাত্র keep ব্যবহার করলে ম্যাচ করা ক্লাসগুলিতে কোনো ধরনের অপটিমাইজেশন হতে পারে না।
keepclasseswithmembers একটি ক্লাস এবং তার নির্দিষ্ট মেম্বারসমূহ কেবল তখনই সংরক্ষিত হয়, যখন ক্লাসটিতে ক্লাস স্পেসিফিকেশন অনুযায়ী সমস্ত মেম্বার থাকে।
keepclassmembernames নির্দিষ্ট ক্লাস মেম্বারদের নাম পরিবর্তন করা থেকে বিরত রাখে, কিন্তু ক্লাস বা এর মেম্বারদের অপসারণ করা থেকে বিরত রাখে না।

দ্রষ্টব্য: এই অপশনটির অর্থ প্রায়শই ভুল বোঝা হয়; এর পরিবর্তে সমতুল্য -keepclassmembers,allowshrinking ব্যবহার করার কথা বিবেচনা করুন।
keepnames এটি ক্লাস এবং তাদের সদস্যদের নাম পরিবর্তন করা থেকে বিরত রাখে, কিন্তু অব্যবহৃত বলে বিবেচিত হলে তাদের সম্পূর্ণরূপে মুছে ফেলা থেকে বিরত রাখে না।

দ্রষ্টব্য: এই অপশনটির অর্থ প্রায়শই ভুল বোঝা হয়; এর পরিবর্তে সমতুল্য -keep,allowshrinking ব্যবহার করার কথা বিবেচনা করুন।
keepclasseswithmembernames এটি ক্লাস এবং তাদের নির্দিষ্ট মেম্বারদের নাম পরিবর্তন করা থেকে বিরত রাখে, তবে শুধুমাত্র যদি মেম্বারগুলো চূড়ান্ত কোডে বিদ্যমান থাকে। এটি কোড অপসারণে বাধা দেয় না।

দ্রষ্টব্য: এই অপশনটির অর্থ প্রায়শই ভুল বোঝা হয়; এর পরিবর্তে সমতুল্য -keepclasseswithmembers,allowshrinking ব্যবহার করার কথা বিবেচনা করুন।

সঠিক রাখার বিকল্পটি বেছে নিন

আপনার অ্যাপের জন্য সঠিক অপটিমাইজেশন নির্ধারণ করতে সঠিক কিপ অপশন বেছে নেওয়া অত্যন্ত গুরুত্বপূর্ণ। কিছু কিপ অপশন কোড সঙ্কুচিত করে, যা এমন একটি প্রক্রিয়া যার মাধ্যমে অপ্রয়োজনীয় কোড মুছে ফেলা হয়, আবার অন্যগুলো কোডকে অস্পষ্ট করে বা নাম পরিবর্তন করে। নিচের সারণিতে বিভিন্ন কিপ অপশনের কার্যক্রমগুলো উল্লেখ করা হলো:

বিকল্পটি রাখুন মনোবিদের ক্লাস শ্রেণীগুলিকে অস্পষ্ট করে তোলে সদস্যদের সঙ্কোচন সদস্যদের বিভ্রান্ত করে
keep
keepclassmembers
keepclasseswithmembers
keepnames
keepclassmembernames
keepclasseswithmembernames

বিকল্প সংশোধক রাখুন

একটি কিপ অপশন মডিফায়ার একটি কিপ রুলের পরিধি এবং আচরণ নিয়ন্ত্রণ করতে ব্যবহৃত হয়। আপনি আপনার কিপ রুলে ০ বা তার বেশি কিপ অপশন মডিফায়ার যোগ করতে পারেন।

একটি 'keep' অপশন মডিফায়ারের সম্ভাব্য মানগুলো নিম্নলিখিত সারণিতে বর্ণনা করা হলো:

মূল্য বর্ণনা
allowoptimization নির্দিষ্ট উপাদানগুলোর অপ্টিমাইজেশন করার সুযোগ দেয়। তবে, নির্দিষ্ট উপাদানগুলোর নাম পরিবর্তন বা অপসারণ করা হয় না।
allowobfuscation নির্দিষ্ট উপাদানগুলোর নাম পরিবর্তন করা যায়। তবে, উপাদানগুলো সরানো বা অন্য কোনোভাবে অপ্টিমাইজ করা যায় না।
allowshrinking যদি R8 নির্দিষ্ট উপাদানগুলির কোনো উল্লেখ খুঁজে না পায়, তবে সেগুলি অপসারণ করার অনুমতি দেয়। তবে, উপাদানগুলির নাম পরিবর্তন করা হয় না বা অন্য কোনোভাবে অপ্টিমাইজ করা হয় না।
includedescriptorclasses সংরক্ষিত মেথড (প্যারামিটার টাইপ ও রিটার্ন টাইপ) এবং ফিল্ড (ফিল্ড টাইপ)-এর ডেসক্রিপ্টরে উপস্থিত সমস্ত ক্লাস রাখতে R8-কে নির্দেশ দেয়।
allowaccessmodification এটি R8-কে অপটিমাইজেশন প্রক্রিয়ার সময় ক্লাস, মেথড এবং ফিল্ডের অ্যাক্সেস মডিফায়ার ( public , private , protected ) পরিবর্তন (সাধারণত প্রসারিত) করার অনুমতি দেয়।
allowrepackage এর ফলে R8 ক্লাসগুলোকে ডিফল্ট (রুট) প্যাকেজসহ বিভিন্ন প্যাকেজে স্থানান্তর করতে পারে।

শ্রেণী নির্দিষ্টকরণ

প্রতিটি কিপ রুলে আপনাকে অবশ্যই একটি ক্লাস (ইন্টারফেস, এনাম এবং অ্যানোটেশন ক্লাস সহ) নির্দিষ্ট করতে হবে। আপনি ঐচ্ছিকভাবে অ্যানোটেশনের উপর ভিত্তি করে, একটি সুপারক্লাস বা ইমপ্লিমেন্টেড ইন্টারফেস উল্লেখ করে, অথবা ক্লাসটির অ্যাক্সেস মডিফায়ার উল্লেখ করে রুলটিকে সীমাবদ্ধ করতে পারেন। java.lang.String এর মতো java.lang নেমস্পেসের ক্লাস সহ সমস্ত ক্লাস অবশ্যই তাদের সম্পূর্ণ কোয়ালিফাইড জাভা নাম ব্যবহার করে নির্দিষ্ট করতে হবে। কোন নামগুলো ব্যবহার করা উচিত তা বোঝার জন্য, "জেনারেটেড জাভা নামগুলো পরিদর্শন করুন" অংশে বর্ণিত টুলগুলো ব্যবহার করে বাইটকোড পরিদর্শন করুন।

নিম্নলিখিত উদাহরণটি দেখায় যে কীভাবে MaterialButton ক্লাসটি নির্দিষ্ট করতে হয়:

  • সঠিক: com.google.android.material.button.MaterialButton
  • ভুল: MaterialButton

ক্লাস স্পেসিফিকেশনে একটি ক্লাসের মধ্যে থাকা মেম্বারগুলোকেও নির্দিষ্ট করে দেওয়া হয় , যেগুলো রাখা উচিত। উদাহরণস্বরূপ, নিম্নলিখিত নিয়মটি MyClass ক্লাস এবং someSpecificMethod() মেথডটিকে রাখে:

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

অ্যানোটেশনের উপর ভিত্তি করে ক্লাস নির্দিষ্ট করুন

অ্যানোটেশনের উপর ভিত্তি করে ক্লাস নির্দিষ্ট করতে, অ্যানোটেশনটির সম্পূর্ণ জাভা নামের আগে একটি @ চিহ্ন যুক্ত করুন। উদাহরণস্বরূপ:

-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 *(...);
}

কোটলিন-নির্দিষ্ট মডিফায়ার

R8 কোটলিন-এর নিজস্ব মডিফায়ার যেমন internal এবং suspend সমর্থন করে না। এই ধরনের ফিল্ডগুলো রাখতে নিম্নলিখিত নির্দেশিকা অনুসরণ করুন।

  • internal কোনো ক্লাস, মেথড বা ফিল্ডকে পাবলিক হিসেবে রাখতে হলে, সেটিকে পাবলিক হিসেবে গণ্য করুন। উদাহরণস্বরূপ, নিম্নলিখিত কোটলিন সোর্সটি বিবেচনা করুন:

    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(...);
    }
    

সদস্যের স্পেসিফিকেশন

ক্লাস স্পেসিফিকেশনে ঐচ্ছিকভাবে সংরক্ষিতব্য ক্লাস মেম্বারগুলো অন্তর্ভুক্ত করা যায়। যদি আপনি কোনো ক্লাসের জন্য এক বা একাধিক মেম্বার নির্দিষ্ট করেন, তবে নিয়মটি অন্যান্য মেম্বারদের ক্ষেত্রে প্রযোজ্য হয় না।

টীকাগুলির উপর ভিত্তি করে সদস্যদের নির্দিষ্ট করুন

আপনি অ্যানোটেশনের উপর ভিত্তি করে মেম্বার নির্দিষ্ট করতে পারেন। ক্লাসের মতোই, আপনাকে অ্যানোটেশনের সম্পূর্ণ জাভা নামের আগে @ চিহ্নটি বসাতে হবে। এর ফলে আপনি একটি ক্লাসের মধ্যে শুধুমাত্র সেই মেম্বারগুলোকেই রাখতে পারবেন যেগুলো নির্দিষ্ট অ্যানোটেশন দ্বারা চিহ্নিত। উদাহরণস্বরূপ, @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>);

উদাহরণস্বরূপ, নিম্নলিখিত keep rule-টি setLabel() নামের একটি public method রাখে, যা void রিটার্ন করে এবং একটি String গ্রহণ করে।

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

একটি ক্লাসের সমস্ত মেথড মেলানোর জন্য আপনি শর্টকাট হিসেবে <methods> ব্যবহার করতে পারেন, যেমন:

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

রিটার্ন টাইপ এবং প্যারামিটার টাইপের জন্য কীভাবে টাইপ নির্দিষ্ট করতে হয় সে সম্পর্কে আরও জানতে, টাইপস (Types) দেখুন।

নির্মাণকারী

কনস্ট্রাক্টর নির্দিষ্ট করতে <init> ব্যবহার করুন। একটি keep rule-এর মেম্বার স্পেসিফিকেশনে কনস্ট্রাক্টর নির্দিষ্ট করার সিনট্যাক্সটি নিম্নরূপ:

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

উদাহরণস্বরূপ, নিম্নলিখিত keep rule-টি একটি কাস্টম 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>;

উদাহরণস্বরূপ, নিম্নলিখিত keep rule-টি userId নামক একটি private string field এবং STATUS_ACTIVE নামক একটি public static integer field রাখে:

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

একটি ক্লাসের সমস্ত ফিল্ড মেলানোর জন্য আপনি শর্টকাট হিসেবে <fields> ব্যবহার করতে পারেন, যেমন:

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

প্রকারভেদ

এই বিভাগে কিপ রুল মেম্বার স্পেসিফিকেশনে রিটার্ন টাইপ, প্যারামিটার টাইপ এবং ফিল্ড টাইপ কীভাবে নির্দিষ্ট করতে হয় তা বর্ণনা করা হয়েছে। মনে রাখবেন, টাইপগুলো কোটলিন সোর্স কোড থেকে ভিন্ন হলে, সেগুলো নির্দিষ্ট করার জন্য জেনারেটেড জাভা নাম ব্যবহার করতে হবে।

আদিম প্রকার

একটি প্রিমিটিভ টাইপ নির্দিষ্ট করতে, এর জাভা কীওয়ার্ড ব্যবহার করুন। 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);
}

জেনেরিক প্রকার

কম্পাইলেশনের সময়, কোটলিন/জাভা কম্পাইলার জেনেরিক টাইপের তথ্য মুছে ফেলে, তাই যখন আপনি জেনেরিক টাইপ সম্পর্কিত কিপ রুলস লেখেন, তখন আপনাকে অবশ্যই আপনার কোডের কম্পাইল করা রূপকে টার্গেট করতে হবে, মূল সোর্স কোডকে নয়। জেনেরিক টাইপ কীভাবে পরিবর্তিত হয় সে সম্পর্কে আরও জানতে, টাইপ ইরেজার দেখুন।

উদাহরণস্বরূপ, যদি আপনার কাছে 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 সহ একটি keep rule ব্যবহার করে 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 rule-টি ব্যবহার করতে পারেন:

-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>();
}

সদস্যের নামের প্যাটার্ন বাতিল করুন

অ্যান্ড্রয়েড গ্রেডল প্লাগইন (AGP) 9.2-alpha02 থেকে শুরু করে, আপনি আপনার 'কিপ' রুলগুলোর মধ্যে মেম্বার নামের প্যাটার্ন বাতিল করতে পারবেন। এর মাধ্যমে আপনি প্যাটার্নের উপর ভিত্তি করে কোন মেম্বারকে রাখবেন বা বাদ দেবেন তা নির্দিষ্ট করতে পারবেন। কোনো প্যাটার্ন বাতিল করতে, মেম্বার নামের প্যাটার্নটির আগে একটি বিস্ময়সূচক চিহ্ন ( ! ) যোগ করুন।

আপনি এই বৈশিষ্ট্যটি এমন পরিস্থিতিতে ব্যবহার করতে পারেন যেখানে আপনি একটি বৃহত্তর প্যাটার্নের সাথে মেলে এমন বেশিরভাগ সদস্যকে রাখতে চান কিন্তু নির্দিষ্ট কিছুকে বাদ দিতে চান, যেমন শুধুমাত্র পরীক্ষার জন্য উদ্দিষ্ট পদ্ধতিসমূহ।

উদাহরণ:

com.example.MyClass এর 'ForTesting' দিয়ে শেষ হওয়া মেথডগুলো ছাড়া বাকি সব পাবলিক মেথড রাখতে, নিম্নলিখিত নিয়মটি ব্যবহার করুন:

-keepclassmembers class com.example.MyClass {
    public *** !*ForTesting(...);
}

প্যাকেজ-স্তরের ফাংশন

ক্লাসের বাইরে সংজ্ঞায়িত কোনো কোটলিন ফাংশনকে (যাকে সাধারণত টপ লেভেল ফাংশন বলা হয়) রেফারেন্স করতে, কোটলিন কম্পাইলার দ্বারা স্বয়ংক্রিয়ভাবে যুক্ত হওয়া ক্লাসের জন্য তৈরি জাভা নামটি ব্যবহার করা নিশ্চিত করুন। ক্লাসের নামটি হলো কোটলিন ফাইলের নামের শেষে Kt যুক্ত করা। উদাহরণস্বরূপ, যদি আপনার MyClass.kt নামে একটি কোটলিন ফাইল থাকে যা নিম্নরূপে সংজ্ঞায়িত:

package com.example.myapp.utils

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

isEmailValid ফাংশনের জন্য একটি keep rule লিখতে হলে, ক্লাস স্পেসিফিকেশনটিকে অবশ্যই জেনারেটেড ক্লাস 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 rule-টি ব্যবহার করুন:

    -keep class *Example
    
  • যদি আপনি শুধু * ব্যবহার করেন, তাহলে এটি **-এর একটি বিকল্প নাম (alias) হিসেবে কাজ করে। উদাহরণস্বরূপ, নিম্নলিখিত keep নিয়মগুলো সমতুল্য:

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

শর্তসাপেক্ষ রাখার নিয়মাবলী

সাধারণ কিপ রুলের পাশাপাশি, আপনি শর্তসাপেক্ষ কিপ রুলও ব্যবহার করতে পারেন, যা শুধুমাত্র একটি নির্দিষ্ট শর্ত পূরণ হলেই প্রযোজ্য হয়। আপনি -if ফ্ল্যাগ ব্যবহার করে শর্তসাপেক্ষ রুল নির্দিষ্ট করতে পারেন। -if ফ্ল্যাগের পরের কিপ রুলটি তখনই সক্রিয় হয়, যখন -if ফ্ল্যাগে থাকা ক্লাস স্পেসিফিকেশনের সাথে মিল পাওয়া যায়।

শর্তসাপেক্ষ 'কিপ' নিয়মগুলো বিশেষত সেইসব লাইব্রেরি বা কোড প্যাটার্নের ক্ষেত্রে উপযোগী, যেগুলো রিফ্লেকশন ব্যবহার করে। এক্ষেত্রে একটি 'কিপ' নিয়ম কেবল তখনই প্রয়োজন হয়, যখন কোনো নির্দিষ্ট ক্লাস বা মেম্বার উপস্থিত থাকে অথবা কোনো প্যাটার্নের সাথে মেলে। শর্তসাপেক্ষ নিয়ম ব্যবহার করে অপ্রয়োজনীয় কোড ধরে রাখা প্রতিরোধ করার মাধ্যমে আপনার অ্যাপের আকার কমাতে সাহায্য করে।

শর্তসাপেক্ষে কোনো কিছু রাখার নিয়মের সাধারণ সিনট্যাক্সটি নিম্নরূপ:

-if <class_specification_if> <keep_rule>

যদি কোনো -if কন্ডিশনের ক্লাস স্পেসিফিকেশনে ওয়াইল্ডকার্ড (যেমন * বা ** ) থাকে, তাহলে ওয়াইল্ডকার্ডের সাথে মিলে যাওয়া অক্ষরের ক্রমটি ক্যাপচার করা হয়। আপনি পরবর্তী keep রুলে ব্যাকরেফারেন্স ব্যবহার করে এই ক্যাপচার করা স্ট্রিংগুলোকে উল্লেখ করতে পারেন: <1> প্রথম ওয়াইল্ডকার্ড দ্বারা ক্যাপচার করা স্ট্রিংটিকে বোঝায়, <2> দ্বিতীয় ওয়াইল্ডকার্ড দ্বারা ক্যাপচার করা স্ট্রিংটিকে বোঝায়, ইত্যাদি।

উদাহরণস্বরূপ, Jetpack Navigation কম্পোনেন্টটি ডেস্টিনেশনগুলোর মধ্যে টাইপ-সেফ আর্গুমেন্ট পাসিংয়ের জন্য NavArgs ক্লাস তৈরি করে। NavArgsLazy ডেলিগেট ব্যবহার করার সময়, এটি আর্গুমেন্টগুলোকে ডিসিরিয়ালাইজ করার জন্য রিফ্লেকশন ব্যবহার করে তৈরি হওয়া NavArgs ক্লাসের একটি স্ট্যাটিক fromBundle মেথড খুঁজে বের করে এবং কল করে। যদি আপনার অ্যাপ NavArgs ব্যবহার করে, তবে আপনাকে শুধুমাত্র সেই ক্লাসগুলোর জন্য fromBundle মেথডটি রাখতে হবে যেগুলো NavArgs ইন্টারফেসটি ইমপ্লিমেন্ট করে।

আপনি একটি শর্তসাপেক্ষ 'কিপ' রুল ব্যবহার করে নির্দিষ্ট করতে পারেন যে, যদি কোনো ক্লাস androidx.navigation.NavArgs ইমপ্লিমেন্ট করে, তাহলে R8 যেন সেই নির্দিষ্ট ক্লাসের জন্য fromBundle মেথডটি রেখে দেয়:

# If a class implements NavArgs...
-if public class ** implements androidx.navigation.NavArgs
# ...then keep the fromBundle method of that matched class (<1>).
-keepclassmembers public class <1> {
    public static ** fromBundle(android.os.Bundle);
}

এই উদাহরণে, ** হলো -if শর্তের প্রথম এবং একমাত্র ওয়াইল্ডকার্ড। এটি androidx.navigation.NavArgs ইমপ্লিমেন্টকারী যেকোনো ক্লাসের নামের সাথে মেলে। যে স্ট্রিংটির সাথে ** মেলে (এই ক্ষেত্রে, ক্লাসের নাম), সেটি ক্যাপচার করা হয় এবং আপনি পরবর্তী নিয়মে <1> ব্যবহার করে এটিকে উল্লেখ করতে পারেন। সুতরাং, -keepclassmembers নিয়মটি androidx.navigation.NavArgs ইমপ্লিমেন্টকারী যেকোনো ক্লাসের ক্ষেত্রে প্রযোজ্য হয়, যা -if শর্ত দ্বারা মিলেছে। যদি R8 কোনো NavArgs ইমপ্লিমেন্টকারী ক্লাস খুঁজে না পায়, তবে এটি এই keep নিয়মটি উপেক্ষা করে।

এর আরেকটি সাধারণ ব্যবহার হলো Gson-এর মতো JSON সিরিয়ালাইজেশন লাইব্রেরির ক্ষেত্রে। যদি আপনার ডেটা মডেল ক্লাসগুলো কোনো ফিল্ডে Gson-এর @SerializedName অ্যানোটেশন ব্যবহার করে, তাহলে আপনি একটি কন্ডিশনাল রুল ব্যবহার করে সেইসব ক্লাস এবং তার মেম্বারদের সুরক্ষিত করতে পারেন, যা Gson-এর রিফ্লেকশনের জন্য প্রয়োজন:

# If a class has fields annotated with @SerializedName...
-if class ** { @com.google.gson.annotations.SerializedName <fields>; }
# ...then keep that class (<1>), its @SerializedName fields,
# and its constructors for Gson.
-keep class <1> {
    @com.google.gson.annotations.SerializedName <fields>;
    <init>(...);
}

ব্যাকরেফারেন্স স্ট্রিং ক্যাপচার করে, যা ক্লাস নামের সাবস্ট্রিংও হতে পারে যদি কোনো ওয়াইল্ডকার্ড নামের শুধু একটি অংশের সাথে মেলে। উদাহরণস্বরূপ, যদি আপনি -if class com.example.*X* ব্যবহার করেন, R8 X আগের সাবস্ট্রিংটিকে <1> এবং X এর পরের সাবস্ট্রিংটিকে <2> হিসেবে ক্যাপচার করে। নিম্নলিখিত নিয়মটি এটি ব্যবহার করে X ধারণকারী যেকোনো ক্লাস নাম খুঁজে বের করে এবং একটি সংশ্লিষ্ট ক্লাস রাখে যেখানে X Y দ্বারা প্রতিস্থাপন করা হয়:

# If a class like com.example.PrefixXPostfix exists...
-if class com.example.*X*
# ...keep com.example.PrefixYPostfix.
-keep class com.example.<1>Y<2>

প্রতিফলনের জন্য শর্তসাপেক্ষ রাখার নিয়ম।

শর্তসাপেক্ষ 'কিপ' নিয়মের একটি সাধারণ ব্যবহার হলো রিফ্লেকশন পরিচালনা করা, যেখানে রানটাইমে নির্দিষ্ট মেথড বা ক্লাস ডাইনামিকভাবে অ্যাক্সেস করা হয়। উদাহরণস্বরূপ, যদি কোনো লাইব্রেরি আপনার কোডের সাথে ইন্টারঅ্যাক্ট করার জন্য রিফ্লেকশন ব্যবহার করে, তবে সেই লাইব্রেরির কোনো নির্দিষ্ট ফিচার ব্যবহার করলেই কেবল আপনার কিছু মেম্বার 'কিপ' করার প্রয়োজন হতে পারে।

Jetpack Navigation লাইব্রেরি টাইপ-সেফ আর্গুমেন্ট পাসিংয়ের জন্য জেনারেট করা NavArgs ক্লাসগুলিতে একটি স্ট্যাটিক fromBundle মেথড কল করতে NavArgsLazy ডেলিগেট ব্যবহার করে রিফ্লেকশন প্রয়োগ করে। এই মেথডটি যেন শুধুমাত্র NavArgs ইমপ্লিমেন্টেশনগুলোর জন্যই রাখা হয়, এবং প্রতিটি ক্লাসের জন্য নয়, তা নিশ্চিত করতে Jetpack Navigation নিম্নলিখিত শর্তসাপেক্ষ কিপ রুলটি অন্তর্ভুক্ত করে:

# If a class implements NavArgs...
-if public class ** implements androidx.navigation.NavArgs
# ...then keep the fromBundle method of that matched class (<1>).
-keepclassmembers public class <1> {
    public static ** fromBundle(android.os.Bundle);
}

এই নিয়মটি fromBundle সব ক্লাসে রাখার পরিবর্তে অথবা কোন ক্লাসগুলোর জন্য এটি প্রয়োজন তা আপনাকে ম্যানুয়ালি নির্দিষ্ট করার প্রয়োজন না করে, শুধুমাত্র যে ক্লাসগুলোর প্রয়োজন সেগুলোর জন্যই এটি রাখে।

তৈরি করা জাভা নামগুলি পরিদর্শন করুন

কিপ রুল লেখার সময়, ক্লাস এবং অন্যান্য রেফারেন্স টাইপগুলোকে জাভা বাইটকোডে কম্পাইল করার পর তাদের নাম ব্যবহার করে অবশ্যই নির্দিষ্ট করতে হবে (উদাহরণস্বরূপ ক্লাস স্পেসিফিকেশন এবং টাইপস দেখুন)। আপনার কোডের জন্য তৈরি হওয়া জাভা নামগুলো কী তা পরীক্ষা করতে, অ্যান্ড্রয়েড স্টুডিওতে নিম্নলিখিত টুলগুলোর যেকোনো একটি ব্যবহার করুন:

  • এপিকে অ্যানালাইজার
  • Kotlin সোর্স ফাইলটি খোলা থাকা অবস্থায়, Tools > Kotlin > Show Kotlin Bytecode > Decompile- এ গিয়ে বাইটকোড পরীক্ষা করুন।