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

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

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

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

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

Android.bp ফাইলে, optimize প্রপার্টি ব্যবহার করে R8 অপ্টিমাইজেশন কনফিগার করুন android_app এবং ইনস্টল করা যায় এমন java_library টার্গেট (অথবা 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 থাকে, তবে 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 এই টার্গেটে বাইটের কোড রেফারেন্স সহ 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 (বা স্পষ্টভাবে পাবলিক API নাম সংরক্ষণ করুন) ব্যবহার করুন:

  • bootclasspath বা system_server classpath-এ শেয়ার করা লাইব্রেরি: মডিউল (java_library বা java_sdk_library) যা এক্সটার্নাল API প্রকাশ করে রানটাইমে অন্যান্য মডিউল দ্বারা ডাইনামিক লিঙ্ক করা সারফেস (যেমন framework.jar, services.jar বা <uses-library> টার্গেট) অবশ্যই obfuscate: false রাখতে হবে অথবা @KeepForApi বা -keep নিয়মের (bootclasspath টার্গেটের জন্য protect_api_surface: true সহ) মাধ্যমে স্পষ্টভাবে তাদের API সারফেস প্রিজার্ভ করতে হবে, যাতে তাদের স্টাবের বিরুদ্ধে কম্পাইল করা কলাররা রানটাইমে ক্লাস ও মেম্বার নাম সমাধান করতে পারে।

কোনও অ্যাপ্লিকেশনে 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 ও লেআউট রিসোর্স (দেখুন ডিফল্ট প্ল্যাটফর্ম কিপ সংক্রান্ত নিয়ম বুঝুন) দ্বারা জেনারেট করা হয়।
  • টার্গেট করা বিকল্প: যেখানে এন্ট্রি পয়েন্ট অবশ্যই সুরক্ষিত রাখতে হবে, সেখানে ঘোষণা-সাইট সংক্রান্ত অ্যানোটেশন (keepanno, @VisibleForTesting), এক্সপোর্ট করা লাইব্রেরি নিয়ম (export_proguard_flags_files: true) অথবা আলাদা করা .flags ফাইলের উপর স্ট্যাটিক টেস্ট লিঙ্কিং (দেখুন আগেকার 'কিপ' নিয়ম অডিট ও মাইগ্রেট করা) ব্যবহার করুন।

কাস্টম proguard.flags বা keep.xml ফাইল যোগ বা রেখে দেওয়ার আগে, যাচাই করে নিন যে নিয়মটি আগে থেকেই ডিফল্ট বেসলাইন দ্বারা ম্যানেজ করা হয় কিনা অথবা কোড অ্যানোটেশনে মাইগ্রেট করা যায় কিনা।

ডিফল্ট প্ল্যাটফর্মের ডেটা রাখার নিয়ম সম্পর্কে জানুন

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

  • build/make/core/proguard.flags: @com.android.internal.annotations.VisibleForTesting দিয়ে অ্যানোটেট করা ক্লাস ও মেম্বারদের সব প্যাকেজ জুড়ে এবং @VisibleForTesting দিয়ে (androidx.annotation.VisibleForTesting বা com.google.common.annotations.VisibleForTesting) android.**, com.android.** ও com.google.android.** প্যাকেজের মধ্যে সংরক্ষণ করে। এছাড়াও, এটি @TestApi, @Keep (androidx.annotation, android.support.annotation, com.android.internal.annotations), @KeepForWeakReference, @WeaklyReferencedCallback এবং @dalvik.annotation.optimization.** অক্ষুণ্ণ রাখে।
  • build/make/core/proguard_basic_keeps.flags: স্ট্যাক ট্রেস, রানটাইম ভিজিবিলিটি অ্যানোটেশন (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 ফাইল থেকে টেস্ট-ড্রাইভেন কিপ নিয়ম সরান:

  • টেস্টে ইমপ্লিমেন্টেশন লাইব্রেরি লিঙ্ক করুন (static_libs): কোনও android_test অ্যাপের প্যাকেজ-প্রাইভেট বা ইন্টার্নাল ইমপ্লিমেন্টেশন ডিটেলেইলের ব্যবহার করলে, সাজেস্ট করা প্ল্যাটফর্ম আর্কিটেকচার হল অ্যাপ সোর্স ফাইলকে android_library (MyApp.impl) ফোল্ডারে রাখা এবং MyApp ও MyAppTests MyApp.impl দুটিতেই স্ট্যাটিক লিঙ্ক করা:

    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 দিয়ে সেইসব ঘোষণা অ্যানোটেট করুন। কাস্টম .flags ফাইল ছাড়াই গ্লোবাল build/make/core/proguard.flags বেসলাইন @VisibleForTesting android.**, com.android.** ও com.google.android.** প্যাকেজে আইটেম অটোমেটিক রেখে দেয়। (এইসব নেমস্পেসের বাইরের ভেন্ডর মডিউলের জন্য, @UsedByReflection বা এক্সপোর্ট করা লাইব্রেরি রুল ব্যবহার করুন।)

  • প্ল্যাটফর্ম লাইব্রেরি সীমানা জুড়ে trace_references_from ব্যবহার করুন: যখন টেস্ট টার্গেট ইমপ্লিমেন্টেশনকে স্ট্যাটিক লিঙ্ক করতে পারে না (যেমন, সিস্টেম সার্ভার পরিষেবা বা framework-connectivity-এর মতো প্ল্যাটফর্ম JAR-কে এক্সারসাইজ করা টেস্ট), তখন টার্গেট মডিউলে trace_references_from কনফিগার করুন যা টেস্ট সোর্স থাকা Java লাইব্রেরি কম্প্যানিয়ন মডিউলের দিকে পয়েন্ট করে। R8, কম্প্যানিয়ন বাইটকোড দ্বারা রেফারেন্স করা সব ক্লাস ও মেম্বারকে ট্রেস ও রিটেন করে।

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

শেয়ার করা 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,
    },
}

স্ট্যাটিক লিঙ্ক করা my-shared-library সব ডাউনস্ট্রিম android_app এবং লাইব্রেরি টার্গেট অটোমেটিক সেইসব নিয়ম ইনহেরিট করে, তাই ডাউনস্ট্রিম অ্যাপকে সেগুলি ডুপ্লিকেট করতে হয় না। বিভিন্ন ডিরেক্টরি জুড়ে একাধিক মডিউলকে যদি এমন একটি নিয়মের সেট শেয়ার করতে হয় যা কোনও একটি কোড লাইব্রেরির সাথে যুক্ত নয়, তাহলে Android.bp-এ একটি এক্সপ্লিসিট filegroup-এর মাধ্যমে .flags ফাইল প্রকাশ করুন, যাতে মডিউলগুলি প্যাকেজ বাউন্ডারি জুড়ে :my-shared-flags পরিষ্কারভাবে রেফারেন্স করতে পারে। ডাউনস্ট্রিম অ্যাপ অপ্টিমাইজেশন সীমাবদ্ধ না করে লাইব্রেরি কনজিউমার কিপ রুল লেখার বিষয়ে সাধারণ নির্দেশিকা পেতে, লাইব্রেরি লেখকদের জন্য অপ্টিমাইজেশন দেখুন।

ধাপ ৪: সোর্স কোডে ঘোষণাগুলি অ্যানোটেট করা

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

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

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

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

    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 সাবক্লাস ধরে রাখতে বাধ্য করে, যা classpath-এ কোথাও না কোথাও পাওয়া যায়। এর মধ্যে অব্যবহৃত লাইব্রেরি কম্পোনেন্ট ও ডিবাগ অ্যাক্টিভিটি অন্তর্ভুক্ত থাকে।
  • সাজেস্ট করা সমাধান: নিয়মটি মুছে দিন। 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 কোড জুড়ে ডেড মেথড সরানো ও ইনলাইনিং ব্লক হয়ে যায়।
  • সাজেস্ট করা সমাধান: নিয়মটি মুছে দিন। AAPT2 ইতিমধ্যেই কাস্টম View ক্লাসের জন্য কনস্ট্রাক্টর সংরক্ষণ করে যা লেআউট XML ফাইল থেকে ইনফলেটেড হয়। কোড যদি 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 সরিয়ে দিন। Android.bp-এ স্পষ্টভাবে অপ্টিমাইজেশন আচরণ কন্ট্রোল করুন, এর জন্য লিফ টার্গেটে optimize ব্লক (shrink, optimize, obfuscate) ব্যবহার করুন।

কমিট করা নিয়মে ডায়াগনস্টিক ফ্ল্যাগ

# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
  • বিল্ডের ক্ষতি কেন হয়: স্যান্ডবক্সড Soong বিল্ড চলাকালীন ডায়াগনস্টিক ফ্ল্যাগ বিল্ড লগ ভরে দেয় অথবা স্থানীয় পাথে আউটপুট ফাইল লেখার চেষ্টা করে।
  • সাজেস্ট করা সমাধান: কমিট করা .flags ফাইল থেকে এইসব ফ্ল্যাগ মুছে দিন। Soong out/soong/.intermediates/-এর অধীনে মডিউলের ইন্টারমিডিয়েট ডিরেক্টরিতে R8 ম্যাপিং ও ব্যবহারের আউটপুট, যেমন proguard_dictionary ও proguard_usage.zip, অটোমেটিক লেখে।

টেস্ট কোডের জন্য প্রোডাকশন কীপ সংক্রান্ত নিয়ম

  • পারফর্ম্যান্সের উপর এর প্রভাব: শুধুমাত্র টেস্ট অ্যাক্সেসের জন্য কাস্টম -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) বা 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-এর মাধ্যমে কিপ রেডিয়াস ও রুল সাবসাম্পশন বিশ্লেষণ করা

ওপেন-সোর্স 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 ফাইলে নিয়ম সংক্রান্ত মেট্রিক রেকর্ড করতে নির্দেশ দেয়। প্রতিটি keep rule ও keepanno অ্যানোটেশনের জন্য, R8 এগুলি রেকর্ড করে:

    • অবিলম্বে রেখে দেওয়ার ব্যাসার্ধ: নিয়মের মাধ্যমে রেখে দেওয়া নির্দিষ্ট ক্লাস, ফিল্ড ও পদ্ধতি এবং তার সাথে সাথে ছোট করা, অপ্টিমাইজ করা বা অস্পষ্ট করার বিরুদ্ধে সেটির প্রয়োগ করা নির্দিষ্ট বিধিনিষেধ।
    • নিয়ম সাবসাম্পশন: কোন কোন অন্যান্য কিপ নিয়ম বা অ্যানোটেশন ইতিমধ্যেই একই আইটেম ধরে রেখেছে। কোনও কাস্টম নিয়ম যদি কোনও AAPT2 নিয়ম বা গ্লোবাল বেসলাইন নিয়মের মধ্যে সম্পূর্ণভাবে অন্তর্ভুক্ত থাকে, তাহলে আপনি সেটি নিরাপদে মুছে দিতে পারেন।
    • প্যাকেজ-ব্যাপী ও গ্লোবাল নিয়ম: এমন নিয়ম যা প্যাকেজ-ব্যাপী ওয়াইল্ডকার্ড ব্যবহার করে বা গ্লোবাল কনফিগারেশন ডাইরেক্টিভ প্রয়োগ করে।
  2. r8keepradius.pb আউটপুটকে ইন্ট্যার‍্যাক্টিভ HTML রিপোর্টে কনভার্ট করো, এর জন্য KeepRadiusHtmlReportGenerator (prebuilts/r8/r8.jar-এ বান্ডেল করা) ব্যবহার করো:

    # Generate an HTML report for a single module
    INTERMEDIATES=out/soong/.intermediates/packages/apps/<APP_NAME>/<APP_NAME>
    java -cp prebuilts/r8/r8.jar \
        com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \
        $INTERMEDIATES/android_common/r8keepradius.pb \
        keep_radius_report.html
    
    # Scan all built modules under out/soong/.intermediates and generate
    # per-module HTML reports plus an aggregate keepradius.html summary
    java -cp prebuilts/r8/r8.jar \
        com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \
        out/soong/.intermediates \
        out/keep_radius_reports
    

    কোনও ডিরেক্টরি দেওয়া হলে, KeepRadiusHtmlReportGenerator সব *keepradius*.pb ফাইল ঘুরে দেখে, প্রতিটি মডিউলের জন্য HTML রিপোর্ট তৈরি করে এবং out/keep_radius_reports/keepradius.html লাইভ আইটেমের সারাংশ গণনা, রাখা আইটেমের গণনা এবং বিল্ড জুড়ে সর্বোচ্চ-রেডিয়াস কিপ নিয়ম তৈরি করে। তৈরি করা রিপোর্টে, কোড ছোট করা, অপ্টিমাইজ করা, এবং কোড অস্পষ্ট করা সংক্রান্ত স্কোর ব্যাখ্যা করা সম্পর্কে আরও জানতে, R8 কনফিগারেশন অ্যানালাইজার ব্যবহার করুন দেখুন।

এছাড়াও, Soong-এ build/soong/java/dex.go-এ দুটি অতিরিক্ত R8 ডায়াগনস্টিক এনভায়রনমেন্ট ভেরিয়েবল কাজ করে:

  • R8_DUMP_INPUT=true: মডিউলের ইন্টারমিডিয়েট ডিরেক্টরিতে r8inputs.zip লেখে, এতে সমস্ত ইনপুট JAR, লাইব্রেরি JAR এবং স্ট্যান্ডঅলোন R8 রিপ্রোডাকশনের জন্য মার্জ করা ProGuard কনফিগারেশন থাকে।
  • 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/-এর অধীনে অন্তর্ভুক্ত থাকে। এই স্ট্যাটিক অ্যানালিসিস টুল, AAPT2 বা গ্লোবাল প্ল্যাটফর্ম বেসলাইন, ওয়াইল্ডকার্ড, -dontoptimize ওভাররাইড এবং শুধুমাত্র পরীক্ষার জন্য প্রযোজ্য কিপ রুল দ্বারা আগে থেকেই কভার করা নিয়মগুলিকে ফ্ল্যাগ করতে, একটি রেপোজিটরি জুড়ে Android.bp, proguard.flags এবং keep.xml ফাইল স্ক্যান করে:

# 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 এবং R8_DUMP_KEEP_RADIUS=true-এর সাথে KeepRadiusHtmlReportGenerator মিলিয়ে বিল্ডের প্রতিটি নিয়মের সঠিক ক্লাস, ফিল্ড ও মেথড রিটেনশন রেডিয়াস পরিমাপ করতে পারে।