R8-এর মাধ্যমে প্ল্যাটফর্ম কোড ও রিসোর্স অপ্টিমাইজ করা

Android প্ল্যাটফর্ম বিল্ড সিস্টেম (Soong) R8 কম্পাইলারকে টার্গেটে রান করে যা DEX বাইটকোড কম্পাইল করে, যার মধ্যে Android অ্যাপ (android_app), টেস্ট (android_test) এবং compile_dex: true সহ ইনস্টলযোগ্য Java লাইব্রেরি (যেমন services.jar) অন্তর্ভুক্ত, যাতে অব্যবহৃত কোড এবং রিসোর্স সঙ্কুচিত, অপ্টিমাইজ এবং স্ট্রিপ করা যায়। স্ট্যাটিক লাইব্রেরিতে (android_library এবং স্ট্যাটিক java_library), Soong সরাসরি R8 রান করে না, তবে ডাউনস্ট্রিম DEX টার্গেটে কনজিউমার কিপ রুল (export_proguard_flags_files: true) অ্যাটাচ ও প্রোপাগেট করতে optimize প্রপার্টি ব্লক ব্যবহার করে যা তাদের স্ট্যাটিক্যালি লিঙ্ক করে।

সিস্টেম ইমেজ বা ভেন্ডর প্যাকেজ তৈরি করা প্ল্যাটফর্ম ইঞ্জিনিয়ারদের জন্য, অত্যধিক বিস্তৃত নিয়ম বাদ দিলে তা সরাসরি সিস্টেমের স্বাস্থ্যকে উন্নত করে:

  • ছোট সিস্টেম পার্টিশন ফুটপ্রিন্ট: R8, /system, /system_ext, /product এবং /vendor-এ APK ও JAR প্যাকেজ করার আগে ডেড ক্লাস, পদ্ধতি এবং রিসোর্স সরিয়ে দেয়।
  • ছোট কম্পাইল করা আর্টিফ্যাক্ট: কম DEX মেথড মানে হল বিল্ড-টাইম বা অন-ডিভাইস কম্পাইলেশনের সময় dex2oat-এর মাধ্যমে জেনারেট করা ছোট .odex এবং .vdex ফাইল।
  • রানটাইম মেমরি প্রেসার কমানো: কারণ Android, ডিমান্ড-পেজড mmap কল ব্যবহার করে প্রসেস মেমরিতে .odex, .vdex এবং APK রিসোর্স টেবিল ম্যাপ করে, ছোট বাইনারি অ্যাপ স্টার্টআপের সময় প্রধান পৃষ্ঠা সংক্রান্ত ত্রুটি কমায় এবং প্রসেস জুড়ে রেসিডেন্ট কোড এবং রিসোর্স মেমরি কমায়। কম্পাইল করা অ্যাপ কোড কীভাবে ডিভাইসের মেমরিকে প্রভাবিত করে সেই সম্পর্কে আরও তথ্যের জন্য, অ্যাপ কোড হল মেমরি দেখুন।
  • আরও কার্যকর সম্পূর্ণ-প্রোগ্রাম অপ্টিমাইজেশন: ন্যারো কিপ কনস্ট্রেন্ট R8 ইনলাইন পদ্ধতি, ডেভার্চুয়ালাইজ ইন্টারফেস ও ভার্চুয়াল কল, অব্যবহৃত ফিল্ড ছাঁটাই এবং ক্লাস জুড়ে কনস্ট্যান্ট প্রোপাগেট করতে দেয়।

Soong-এ R8 অপ্টিমাইজেশন কনফিগার করুন

Android.bp ফাইলে, android_app এবং ইনস্টলযোগ্য java_library টার্গেটে (অথবা android_library এবং স্ট্যাটিক java_library মডিউলে কনজিউমার কিপ নিয়ম এক্সপোর্ট করতে) optimize প্রপার্টি ব্লক ব্যবহার করে R8 অপ্টিমাইজেশন কনফিগার করুন:

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 সেট করা থাকে, তবে DEX সাইজ কমাতে এবং আরও গভীর অপ্টিমাইজেশন আনলক করতে যেখানে সম্ভব সেখানে এটি true-এ সেট করুন (যেখানে সম্ভব সেখানে অস্পষ্টতা চালু করুন দেখুন)।
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 সরাসরি DEX-কম্পাইলিং ডিপেন্ডেন্ট টার্গেটের তালিকা (instrumentation_for বা libs-এর মাধ্যমে কানেক্ট করা) যার বাইটকোড এই টার্গেটে রেফারেন্স R8 অটোমেটিক ট্রেস করে এবং রেখে দেয় (এছাড়াও override_android_app এবং override_android_test ভেরিয়েন্ট জুড়ে প্রোপাগেট করা হয় এবং .impl স্প্লিটের সাথে পেয়ার করা হলে নন-ডিপেন্ডেন্ট কম্প্যানিয়ন জাভা লাইব্রেরির জন্য সাপোর্ট করা হয়)।
allow_traced_references সরাসরি DEX-কম্পাইলিং নির্ভর টার্গেটকে (instrumentation_for বা libs-এর মাধ্যমে কানেক্ট করা, যেমন ক্রস-রিপোজিটরি বা ভেন্ডর টেস্ট যা trace_references_from-এ তালিকাভুক্ত করা যায় না) trace_references: true সেট করে এই টার্গেটে তাদের বাইটকোড রেফারেন্স ট্রেস করার জন্য চালু করার অনুমতি দেয়।
trace_references allow_traced_references: true সহ কোনও টার্গেটের সরাসরি DEX-কম্পাইলিং ডিপেন্ডেন্টে true-এ সেট করা হলে, এই মডিউলে R8 tracereferences রান করে এবং জেনারেট করা কিপ রুল টার্গেট মডিউলে পাস করে।
protect_api_surface --protect-api-surface থেকে R8-এ পাস করে। java_sdk_library-এ সেট করা হলে, এটি অটোমেটিক Metalava API কিপ রুল তৈরি করে এবং সব চালু থাকা API স্কোপ (পাবলিক, সিস্টেম, মডিউল-লাইব্রেরি, সিস্টেম-সার্ভার এবং টেস্ট) জুড়ে ইমপ্লিমেন্টেশন লাইব্রেরিতে অ্যাটাচ করে।

স্ট্যাটিক লাইব্রেরি (android_library, java_library এবং AAR ইমপোর্ট) Android.bp-এ required_downstream_optimization: { enabled: true, optimize: true } সেট করতে পারে যাতে যেকোনও ডাউনস্ট্রিম DEX-কম্পাইলিং টার্গেট যা স্ট্যাটিক্যালি তাদের লিঙ্ক করে (সরাসরি বা ট্রানজিটিভভাবে) R8 সঙ্কোচন এবং বাইটকোড অপ্টিমাইজেশন চালু করতে পারে। Soong এটি অটোমেটিক চালু করে androidx.compose.runtime_runtime যাতে Jetpack Compose-এর সাথে লিঙ্ক করা টার্গেট 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 (বা স্পষ্টভাবে পাবলিক API নাম সংরক্ষণ করুন) রাখুন:

  • bootclasspath বা system_server ক্লাসপাথে শেয়ার করা লাইব্রেরি: রান টাইমে অন্যান্য মডিউল দ্বারা ডাইনামিকভাবে লিঙ্ক করা এক্সটার্নাল API সারফেস প্রকাশ করে এমন মডিউল (java_library বা java_sdk_library) (যেমন framework.jar, services.jar বা <uses-library> টার্গেট) হয় obfuscate: false রাখতে হবে অথবা protect_api_surface: true, @KeepForApi বা -keep নিয়ম ব্যবহার করে তাদের API সারফেস স্পষ্টভাবে সংরক্ষণ করতে হবে যাতে তাদের স্টাবের বিরুদ্ধে কম্পাইল করা কলার রান টাইমে ক্লাস এবং মেম্বারের নাম সমাধান করতে পারে। java_sdk_library মডিউলে, protect_api_surface: true সেটিং অটোমেটিক Metalava ProGuard-এর নিয়ম তৈরি করে এবং সব চালু থাকা API স্কোপ (পাবলিক, সিস্টেম, মডিউল-লাইব্রেরি, সিস্টেম-সার্ভার এবং টেস্ট) জুড়ে ইমপ্লিমেন্টেশন লাইব্রেরিতে অ্যাটাচ করে। আপডেট করা যাবে না এমন bootclasspath টার্গেটে (framework-minus-apex, ext এবং framework-location) সাধারণ R8 অপ্টিমাইজেশন চালু করা হলে, সেগুলি framework-hiddenapi-proguard-rules-কেও (hiddenapi_to_proguard-এর মাধ্যমে জেনারেট করা) অন্তর্ভুক্ত করে যাতে অ্যাক্টিভ boot/hiddenapi/*.txt ফাইলে (hiddenapi-max-target-o.txt বাদ দিয়ে) তালিকাভুক্ত মেম্বারদের ধরে রাখা যায়।

কোনও অ্যাপ্লিকেশনে obfuscate: true চালু করার আগে, মাইগ্রেশন সংক্রান্ত নিম্নলিখিত পূর্বশর্তগুলি যাচাই করুন:

  • এক্সটার্নাল টেস্ট APK ডিপেন্ডেন্সি: যদি কোনও এক্সটার্নাল android_test APK instrumentation_for: "MyApp" সেট করে এবং সরাসরি MyApp-এর ইন্টার্নাল ক্লাস বা মেথডকে ইনভোক করে, তাহলে সেইসব সিম্বলের নাম পরিবর্তন করলে টেস্ট রান টাইমে NoSuchMethodError বা NoClassDefFoundError হয়। trace_references_from কনফিগার করুন (অথবা trace_references: true-এর সাথে allow_traced_references), MyApp.impl-কে স্ট্যাটিক্যালি লিঙ্ক করে সেল্ফ-ইনস্ট্রুমেন্টিং APK হিসেবে টেস্ট স্ট্রাকচার করুন অথবা @VisibleForTesting-এর সাথে টেস্ট হুক অ্যানোটেট করুন (ধাপ ২: টেস্ট-চালিত কিপ রুল মাইগ্রেট করুন দেখুন) যাতে টেস্টের জন্য প্রয়োজনীয় ইন্টার্নাল সিম্বলগুলি অ্যাপের বাকি অংশ অস্পষ্ট করার সময় সংরক্ষিত থাকে।
  • স্ট্রিং-ভিত্তিক রিফ্লেকশন ও 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 ফাইল যোগ বা রেখে দেওয়ার আগে, নিয়মটি আগে থেকেই ডিফল্ট বেসলাইন দ্বারা ম্যানেজ করা হচ্ছে কিনা অথবা কোড অ্যানোটেশনে মাইগ্রেট করা যাবে কিনা তা যাচাই করুন।

ডিফল্ট প্ল্যাটফর্ম রাখার নিয়ম সম্পর্কে বোঝা

DEX-কম্পাইলিং টার্গেটে (android_app, android_test এবং ইনস্টল করা যায় এমন java_library মডিউল) প্রতিটি R8 ইনভোকেশনে Soong অটোমেটিক নিম্নলিখিত গ্লোবাল বেসলাইন নিয়ম পাস করে, যা build/soong/java/dex.go-এ কনফিগার করা আছে:

  • 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 ফিল্ড এবং protobuf 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: সাধারণ নাল চেক হেল্পার কল (com.google.common.base.Preconditions.checkNotNull এবং dagger.internal.Preconditions.checkNotNull*) সংক্ষিপ্ত বাইটকোড নাল চেক দিয়ে প্রতিস্থাপন করে। ফ্রেমওয়ার্ক API বাউন্ডারি জুড়ে ব্যতিক্রমী মেসেজ স্পষ্টভাবে সংরক্ষণ করতে এই ফাইল ইচ্ছাকৃতভাবে Objects.requireNonNull বাদ দেয়।
  • build/make/core/proguard/enumvalues.flags: বিল্ড কনফিগারেশন দ্বারা বন্ধ না করা পর্যন্ত enum ধরনের উপর values এবং valueOf পদ্ধতি বজায় রাখে।
  • AAPT2 অটোমেটিক জেনারেট করা নিয়ম: AAPT2 মার্জ করা AndroidManifest.xml, লেআউট XML এবং প্রেফারেন্স XML ফাইল পরীক্ষা করে দেখে যাতে প্রতিটি রেজিস্টার করা Activity, Service, BroadcastReceiver, ContentProvider, BackupAgent, Application, কাস্টম View সাবক্লাস, Preference সাবক্লাস এবং XML android:onClick পদ্ধতির জন্য সঠিক নিয়ম বজায় রাখা যায়।

আগে থেকে থাকা রাখার নিয়ম অডিট ও মাইগ্রেট করা

কোনও প্ল্যাটফর্ম রিপোজিটরিতে আগে থেকে থাকা proguard.flags বা keep.xml ফাইল অডিট করার সময়, নিম্নলিখিত চার-ধাপের ক্রম অনুযায়ী প্রতিটি নিয়ম মূল্যায়ন করুন:

ধাপ ১: অপ্রয়োজনীয় বা পুরনো নিয়ম মুছে দিন

গ্লোবাল বেসলাইন, AAPT2 ম্যানিফেস্ট এবং লেআউট রুল বা R8 ডিফল্ট দ্বারা আগে থেকেই কভার করা হয়েছে এমন রুল মুছে দিন, এর পাশাপাশি এমন রুলও মুছে দিন যা আর নেই এমন ক্লাস বা প্যাকেজকে রেফার করে।

.flags ফাইলের সব নিয়ম অপ্রয়োজনীয় হলে, ফাইলটি মুছে দিন এবং Android.bp থেকে proguard_flags_files সরিয়ে দিন। বাকি optimize ব্লক যদি শুধু ডিফল্ট সেটিংস রিস্টেট করে, তাহলে অপ্রয়োজনীয় ব্লক সরিয়ে দিন এবং bpfmt -w Android.bp দিয়ে বিল্ড ফাইল ফর্ম্যাট করুন। কোনও প্যাকেজ ক্লিন-আপ করার সময়, সাবডিরেক্টরিতে (যেমন, Kotlin টার্গেট ভেরিয়েন্ট) কম্প্যানিয়ন মডিউল একই ফ্ল্যাগ ফাইল রেফারেন্স করে কিনা তা চেক করুন এবং একসাথে সেগুলি আপডেট করুন।

ধাপ ২: টেস্ট-ড্রিভেন রাখার নিয়ম মাইগ্রেট করুন

নিম্নলিখিত প্যাটার্নের মধ্যে একটি ব্যবহার করে কাস্টম প্রোডাকশন .flags ফাইল থেকে টেস্ট-ড্রিভেন কিপ রুল সরান:

  • টেস্ট বা কম্প্যানিয়ন টার্গেট থেকে রেফারেন্স ট্রেস করা (trace_references_from বা allow_traced_references): টার্গেট এবং সরাসরি নির্ভরশীল মডিউল, দুটিই যখন নিজস্ব DEX (android_app, android_test, বা dexed java_library) কম্পাইল করে এবং instrumentation_for বা libs-এর মাধ্যমে কানেক্ট করা থাকে, তখন Soong, নির্ভরশীল মডিউলে R8 tracereferences চালাতে পারে এবং তৈরি করা নিয়ম টার্গেটের R8 কম্পাইলেশনে ফিড করতে পারে। এর জন্য কোনও ডিপেন্ডেন্সি সাইকেল তৈরি করতে হয় না বা ম্যানুয়ালি .impl লাইব্রেরি স্প্লিট করতে হয় না:

    • একই-রিপোজিটরি টেস্ট (trace_references_from): টার্গেট মডিউলের উপর সরাসরি instrumentation_for বা libs নির্ভরশীলদের তালিকা তৈরি করুন (এটি override_android_app এবং override_android_test ভেরিয়েন্ট জুড়ে অটোমেটিক প্রোপাগেট করে এবং .impl স্প্লিটের সাথে পেয়ার করা হলে, নির্ভরশীল নয় এমন কম্প্যানিয়ন Java লাইব্রেরিতে সহায়তা করা চালিয়ে যায়):

      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"],
    }
    

    এই ৩-মডিউল প্যাটার্ন MyAppTests-কে ইন্টার্নাল ক্লাসে সম্পূর্ণ অ্যাক্সেস সহ সেল্ফ-ইনস্ট্রুমেন্টিং টেস্ট হিসেবে রান করতে দেয় এবং একই সাথে MyApp-কে প্রোডাকশন এন্ট্রি পয়েন্টের বিরুদ্ধে সম্পূর্ণভাবে সঙ্কুচিত ও অস্পষ্ট করতে দেয়।

  • টেস্ট হুককে @VisibleForTesting: দিয়ে অ্যানোটেট করুন। কোনও এক্সটার্নাল টেস্ট APK যদি android_app টার্গেটে শুধুমাত্র কয়েকটি ইন্টার্নাল মেথড বা কনস্ট্রাক্টরকে কল করে, তাহলে আপনি সেইসব ডিক্লারেশনকে @VisibleForTesting দিয়ে অ্যানোটেট করতে পারবেন। গ্লোবাল build/make/core/proguard.flags বেসলাইন, কাস্টম .flags ফাইল ছাড়াই @VisibleForTesting আইটেম android.**, com.android.** এবং com.google.android.** প্যাকেজে অটোমেটিক রেখে দেয়। (এইসব নেমস্পেসের বাইরে ভেন্ডর মডিউলের জন্য, trace_references_from, @UsedByReflection বা এক্সপোর্ট করা লাইব্রেরি নিয়ম ব্যবহার করুন।) trace_references_from বা trace_references-এর পরিবর্তে @VisibleForTesting-এর মাধ্যমে হ্যান্ড-অ্যানোটেট করার সময়, Mockito mock(...) বা spy(...)-এ পাস করা ক্লাসও অ্যানোটেট করুন (শুধু স্টাবড পদ্ধতি নয়), কারণ অন-ডিভাইস mockito-target (dexmaker-mockito) রানটাইম সাবক্লাস জেনারেট করে এবং R8 সাবক্লাস করা হয়নি এমন ক্লাস final (MockitoException: Can't mock/spy class ... - final class) হিসেবে চিহ্নিত করতে পারে।

  • ওভারল্যাপ করা স্ট্যাটিক লাইব্রেরি (jarjar_rules) এড়িয়ে চলুন বা আবার প্যাক করুন: যখন android_app এবং তার instrumentation_for টেস্ট APK, উভয়ই স্ট্যাটিক্যালি একই লাইব্রেরি লিঙ্ক করে (সরাসরি, যেমন guava, বা ট্রানজিটিভলি), tracereferences টেস্টে সংজ্ঞায়িত সেই ক্লাসগুলি দেখে এবং অ্যাপের জন্য কোনও'রাখুন' নিয়ম প্রকাশ করে না এবং রান টাইমে অ্যাপের পেরেন্ট ক্লাসলোডার, টেস্টের কপিকে তার R8-শ্রিঙ্ক করা ক্লাসের সাথে শ্যাডো করে, যার ফলে IllegalAccessError বা NoSuchMethodError হয়। টেস্টের জন্য নিজস্ব বান্ডেল করা কপির প্রয়োজন না হলে, সংঘর্ষ এড়াতে টেস্টের static_libs থেকে ডুপ্লিকেটটি সরান (অথবা libs-এর মাধ্যমে লিঙ্ক করুন); অন্যথায়, jarjar_rules ব্যবহার করে মডিউল-নির্দিষ্ট প্রিফিক্সের অধীনে অ্যাপের ওভারল্যাপ করা স্ট্যাটিক লাইব্রেরি প্যাকেজগুলি আবার প্যাকেজ করুন।

ধাপ ৩: মালিকানা থাকা লাইব্রেরি থেকে নিয়ম এক্সপোর্ট করা

শেয়ার করা 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 অটোমেটিক সেইসব নিয়ম ইনহেরিট করে, তাই ডাউনস্ট্রিম অ্যাপকে সেগুলি ডুপ্লিকেট করতে হয় না। যখন বিভিন্ন ডিরেক্টরি জুড়ে একাধিক মডিউলকে অবশ্যই একটি নিয়ম সেট শেয়ার করতে হবে যা একটি একক কোড লাইব্রেরির সাথে আবদ্ধ নয়, তখন Android.bp-এ একটি স্পষ্ট filegroup-এর মাধ্যমে .flags ফাইলটি প্রকাশ করুন যাতে মডিউলগুলি প্যাকেজ সীমানা জুড়ে পরিষ্কারভাবে :my-shared-flags রেফারেন্স করতে পারে। ডাউনস্ট্রিম অ্যাপ অপ্টিমাইজেশন সীমাবদ্ধ না করে লাইব্রেরি কনজিউমার কিপ রুল লেখার বিষয়ে সাধারণ নির্দেশনার জন্য, লাইব্রেরি লেখকদের জন্য অপ্টিমাইজেশন দেখুন।

ধাপ ৪: সোর্স কোডে ডিক্লারেশন অ্যানোটেট করা

রিফ্লেকশন বা JNI-এর মাধ্যমে কোনও ক্লাস, মেথড, কনস্ট্রাক্টর বা ফিল্ড অ্যাক্সেস করা হলে এবং AAPT2 বা গ্লোবাল বেসলাইন দ্বারা কভার করা না হলে, .flags ফাইলে থাকা ডিটাচ করা -keep নিয়মগুলি অ্যানোটেশন রাখার নির্দেশিকা (keepanno Javadoc রেফারেন্স দেখুন) থেকে সোর্স অ্যানোটেশন দিয়ে পরিবর্তন করুন:

  • @UsesReflection: আপনি রিফ্লেকশন পারফর্ম করা কোড কন্ট্রোল করলে এই অ্যানোটেশন বেছে নিন। কোন টার্গেট ক্লাস, পদ্ধতি বা ফিল্ড ডায়নামিকভাবে অ্যাক্সেস করা হয়েছে তা ঘোষণা করতে, রিফ্লেকশন কল সাইটে এটি রাখুন। কারণ @UsesReflection অটোমেটিক একটি পূর্বশর্ত এনকোড করে যে অ্যানোটেট করা কল সাইট নিজেই অ্যাক্সেসযোগ্য, R8 কল সাইট অব্যবহৃত থাকলে কলার এবং রিফ্লেক্টিভ টার্গেট উভয়কেই ছেঁটে ফেলে।
  • @UsedByReflection: ক্লাস, পদ্ধতি, ফিল্ড বা কনস্ট্রাক্টরে রাখুন যা এক্সটার্নাল কোড বা লাইব্রেরি দ্বারা রিফ্লেক্টিভভাবে ইনস্ট্যান্টিয়েট বা ইনভোক করা হয়, যেমন Bundle বা Settings কী থেকে Class.forName দিয়ে লোড করা ক্লাস বা ডায়নামিক ক্লাসলোডার জুড়ে লোড করা প্লাগ-ইন ক্লাস। kind (যেমন KeepItemKind.CLASS_AND_METHODS), preconditions (@KeepCondition) এবং প্যারামিটার কনস্ট্রেন্ট নির্দিষ্ট করুন যাতে R8 শুধুমাত্র সঠিক রিফ্লেক্টিভ কন্ট্র্যাক্ট রাখে এবং এখনও ক্লাসের অব্যবহৃত মেম্বারদের অপ্টিমাইজ বা ছাঁটাই করতে পারে। JobService বা BroadcastReceiver-এর মতো ম্যানিফেস্ট-রেজিস্টার করা কম্পোনেন্টে @UsedByReflection যোগ করবেন না, কারণ AAPT2-তে এগুলি আগে থেকেই থাকে।
  • @UsedByNative: GetMethodID, GetStaticMethodID বা GetFieldID ব্যবহার করে C বা C++ JNI কোড থেকে অ্যাক্সেস করা পদ্ধতি বা ফিল্ডে প্লেস করুন।
  • @KeepForApi: লাইব্রেরি API ক্লাস বা মেম্বারদের উপর রাখুন যা ডিস্ট্রিবিউশনের আগে লাইব্রেরি নিজে থেকে সঙ্কুচিত হলে অবশ্যই অক্ষত থাকতে হবে।
  • @Keep (androidx.annotation.Keep): keepanno প্রযোজ্য না হলে ফলব্যাক হিসেবে ব্যবহার করুন। কোনও ক্লাসে @Keep প্রয়োগ করলে, ক্লাস এবং এর সব মেম্বারকে শর্তহীনভাবে রেখে দেওয়া হয়। কোনও পদ্ধতি বা ফিল্ডে @Keep প্রয়োগ করলে তা শর্তহীন এন্ট্রি পয়েন্ট হিসেবে কাজ করে যা মেম্বার ও তার ক্লাসকে ধরে রাখে, এমনকি ক্লাসটি কখনও ইনস্ট্যান্টিয়েট না করা হলেও। অন্যদিকে, keepanno শর্তসাপেক্ষ অ্যাক্সেসযোগ্যতা প্রকাশ করে।

Soong মডিউলে R8 keepanno অ্যানোটেশন ব্যবহার করতে:

  1. Android.bp-এ libs-এর সাথে "keepanno-annotations" যোগ করুন:

    libs: [
        "keepanno-annotations",
    ],
    
  2. com.android.tools.r8.keepanno.annotations.* ইমপোর্ট করুন এবং Java বা Kotlin সোর্স কোডে ডিক্লারেশন বা কল সাইট অ্যানোটেট করুন:

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

    R8 সরাসরি keepanno অ্যানোটেশনকে তার ইন্টার্নাল কিপ রুল মডেলে অনুবাদ করে এবং ফাইনাল DEX আউটপুট থেকে অ্যানোটেশন স্ট্রিপ করে, শূন্য রানটাইম বাইটকোড ওভারহেড যোগ করে।

সাধারণ ভুল এড়িয়ে চলুন

নিম্নলিখিত বিভাগে প্ল্যাটফর্ম কোডে ঘন ঘন proguard.flags এবং keep.xml সমস্যা এবং কীভাবে সেগুলি সমাধান করা যায় তা বর্ণনা করা হয়েছে।

ব্রড কম্পোনেন্ট রাখার নিয়ম

# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
  • কেন এটি পারফর্ম্যান্সের উপর প্রভাব ফেলে: AAPT2-এর সাথে এই নিয়মটি সম্পূর্ণ অপ্রয়োজনীয়। কারণ এটি কম্পোনেন্টটি চূড়ান্ত মার্জ করা AndroidManifest.xml-এ রেজিস্টার করা আছে কিনা তা চেক না করেই ওয়াইল্ডকার্ড ব্যবহার করে, এটি R8-কে ক্লাসপাথের যেকোনও জায়গায় পাওয়া প্রতিটি Activity, Service বা BroadcastReceiver সাবক্লাস ধরে রাখতে বাধ্য করে, যার মধ্যে অব্যবহৃত লাইব্রেরি কম্পোনেন্ট এবং অক্ষম করা ডিবাগ অ্যাক্টিভিটি অন্তর্ভুক্ত।
  • সাজেস্ট করা সমাধান: নিয়মটি মুছে দিন। AAPT2 মার্জ করা ম্যানিফেস্ট পরীক্ষা করে এবং রেজিস্টার করা কম্পোনেন্টের জন্য সঠিক -keep নিয়ম তৈরি করে।

ব্রড XML ক্লিক হ্যান্ডলার রাখার নিয়ম

# Don't do this:
-keepclassmembers class * {
    public void *(android.view.View);
}
  • কেন এটি পারফর্ম্যান্সের উপর প্রভাব ফেলে: মডিউলের প্রতিটি ক্লাসে প্রতিটি public void *(View) পদ্ধতি রাখার ফলে R8 এমন কোনও পদ্ধতিকে স্ট্রিপ বা ইনলাইন করতে পারে না যা একটিমাত্র View প্যারামিটার গ্রহণ করে।
  • সাজেস্ট করা সমাধান: নিয়মটি মুছে দিন। AAPT2 লেআউট XML ফাইল স্ক্যান করে এবং android:onClick অ্যাট্রিবিউট দ্বারা রেফারেন্স করা পদ্ধতির জন্য টার্গেট করা নিয়ম তৈরি করে।

ব্রড ভিউ গেটার এবং সেটার নিয়মগুলি রাখে

# 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 লাইব্রেরি সহ) জুড়ে প্রতিটি গেটার এবং সেটার ধরে রাখতে বাধ্য করে, UI কোড জুড়ে ডেড মেথড সরানো এবং ইনলাইনিং ব্লক করে।
  • সাজেস্ট করা সমাধান: নিয়মটি মুছে দিন। লেআউট XML ফাইল থেকে ইনফ্লেট করা কাস্টম View ক্লাসের জন্য AAPT2 আগে থেকেই কনস্ট্রাক্টর প্রিজার্ভ করে। যদি কোড ObjectAnimator.ofFloat(view, "translationZ", ...)-এর মতো রিফ্লেক্টিভ স্ট্রিং নাম ব্যবহার করে ভিউ প্রপার্টি অ্যানিমেট করে, তাহলে রিফ্লেকশন সম্পূর্ণভাবে এড়াতে স্ট্রিং নামটি টাইপ করা প্রপার্টি রেফারেন্স (View.TRANSLATION_Z বা কাস্টম FloatProperty বা IntProperty ইমপ্লিমেন্টেশন) দিয়ে পরিবর্তন করুন। রিফ্লেক্টিভ প্রপার্টি অ্যাক্সেস এড়ানো না গেলে, নির্দিষ্ট গেটার বা সেটার @UsedByReflection দিয়ে অ্যানোটেট করুন।

ব্ল্যাঙ্কেট অপ্টিমাইজেশন বা অস্পষ্টতা বন্ধ করে দেয়

# 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 নিয়ম যোগ করলে, সেই কোডকে অবফাসকেট না করে প্রোডাকশন বিল্ডে রেখে দিতে হয়। trace_references_from (বা allow_traced_references-এর সাথে trace_references: true) কনফিগার করার সময় অথবা @VisibleForTesting-এর সাথে পদ্ধতি অ্যানোটেট করার সময় ম্যানুয়াল -keep নিয়ম বজায় রাখা এড়িয়ে যায় এবং শুধুমাত্র টেস্ট রেফারেন্স বজায় রাখে, সেইসব চিহ্ন এখনও প্রোডাকশন বাইনারিতে শিপ করা হয়।
  • সাজেস্ট করা সমাধান: trace_references_from (অথবা allow_traced_references এবং trace_references: true) চালু করে কাস্টম টেস্ট -keep রুল বাদ দিন অথবা .impl লাইব্রেরিতে অ্যাপ স্ট্রাকচার করুন যা static_libs ব্যবহার করে সেল্ফ-ইনস্ট্রুমেন্টিং টেস্ট APK-তে লিঙ্ক করা থাকে যাতে প্রোডাকশন 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) বা XML (@raw/foo)-এ আগে থেকেই স্ট্যাটিক্যালি রেফারেন্স করা থাকে, তাহলে শ্রিঙ্কার অটোমেটিক সেগুলি রেখে দেয়, তাই আপনি res/raw/keep.xml মুছে দিতে পারেন।

নিয়মে করা পরিবর্তন যাচাই ও অডিট করা

আপনি যখনই proguard.flags বা keep.xml-এ কিপ রুল সরিয়ে দেন বা ছোট করেন, তখন যাচাই করুন যে মডিউলটি পরিষ্কারভাবে তৈরি হয়েছে, ইউনিট এবং ইন্সট্রুমেন্টেশন টেস্ট পাস করেছে, এবং সমস্ত প্রয়োজনীয় এন্ট্রি পয়েন্ট ধরে রেখেছে।

মডিউল তৈরি ও পরীক্ষা করা

  1. R8 এবং AAPT2 কোনও রেফারেন্স মিসিং সংক্রান্ত সতর্কতা ছাড়াই সম্পূর্ণ হয়েছে কিনা তা যাচাই করতে মডিউলটি পরিষ্কারভাবে তৈরি করুন:

    m <MODULE_NAME>
    
  2. atest ব্যবহার করে মডিউলের জন্য ইউনিট ও ইনস্ট্রুমেন্টেশন টেস্ট রান করুন:

    atest <TEST_MODULE_NAME>
    
  3. সিস্টেম অ্যাপ, বিশেষাধিকারপ্রাপ্ত পরিষেবা বা হার্ডওয়্যার-নির্ভর মডিউলের জন্য, রানটাইম রিফ্লেকশন পাথ, IPC বাইন্ডিং ও রিসোর্স ইনফ্লেশন অনুশীলন করতে টার্গেট ফিজিক্যাল ডিভাইসে বা আপনার ডিভাইস টেস্ট ল্যাবে ইনস্ট্রুমেন্টেশন ও UI টেস্ট চালান।

DEX ও রিসোর্স ডিফারেন্স ভালোভাবে দেখুন

নিয়ম পরিবর্তনের আগে ও পরে কম্পাইল করা 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 classes.jar-এ সরাসরি prebuilts/r8/r8.jar চালানোর সময়, মডিউলের --lib ক্লাসপাথ JAR (android.jar, framework.jar এবং শেয়ার করা libs) এবং AAPT2-এর জেনারেট করা proguard.options নিয়ম AndroidManifest.xml এবং লেআউট XML থেকে পাস করুন। এইসব ইনপুট ছাড়া, R8 ফ্রেমওয়ার্ক সুপারটাইপ (Activity, Service, Phone, Handler) এবং আন্ডার-রিটেন হায়ারার্কি পদ্ধতি সমাধান করতে পারে না।

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-এ বান্ডেল করা) ব্যবহার করে একটি ইন্ট্যার‍্যাক্টিভ HTML রিপোর্টে কনভার্ট করুন:

    # 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 build/soong/java/dex.go-এ অতিরিক্ত R8 ডায়াগনস্টিক ও ইনার-লুপ এনভায়রনমেন্ট ভেরিয়েবল সাপোর্ট করে:

  • R8_DUMP_INPUT=true: মডিউলের ইন্টারমিডিয়েট ডিরেক্টরিতে r8inputs.zip লেখে, যেখানে সব ইনপুট JAR, লাইব্রেরি JAR এবং স্ট্যান্ডঅ্যালোন R8 রিপ্রোডাকশনের জন্য মার্জ করা ProGuard কনফিগারেশন থাকে।
  • R8_DUMP_PERFETTO_TRACE=true: Perfetto-তে R8 কম্পাইলেশন পাস পরীক্ষা করার জন্য মডিউলের ইন্টারমিডিয়েট ডিরেক্টরিতে r8trace.ptrace লেখে।
  • SOONG_SKIP_TRACE_REFERENCES=true: টেস্ট কোড ইটারেট করার সময় লোকাল ইনার-লুপ বিল্ডের স্পিড বাড়াতে trace_references_from বা trace_references-এর মাধ্যমে কনফিগার করা tracereferences নিয়ম রান করা এড়িয়ে যায়।

লাইব্রেরি গ্রাহক নিয়ম যাচাই করুন

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

দ্রুত সোর্স-ট্রি ট্রায়াজের জন্য প্ল্যাটফর্ম ও OEM টিম pgaudit.py, এক্সপোর্ট করা লাইব্রেরি নিয়ম যাচাই করার জন্য process-keep-rules এবং বিল্ডের প্রতিটি নিয়মের সঠিক ক্লাস, ফিল্ড ও পদ্ধতি ধরে রাখার রেডিয়াস পরিমাপ করার জন্য KeepRadiusHtmlReportGenerator-এর সাথে R8_DUMP_KEEP_RADIUS=true একত্রিত করতে পারে।