اخبار محصول

پیکربندی و عیب‌یابی کردن «قوانین حفظ R8»

زمان خواندن: ۷ دقیقه
مشاهده نمایه Ajesh Pai مشاهده نمایه Ben Weiss
Ajesh Pai و Ben Weiss

در توسعه مدرن Android، ارائه برنامه‌ای کوچک، سریع، و ایمن از انتظارات اساسی کاربر است. ابزار اصلی سیستم ساخت Android برای دستیابی به این هدف، بهینه‌ساز R8 است، کامپایلری که کدهای مرده و حذف منابع را برای کوچک کردن، تغییر نام کد یا کوچک‌سازی، و بهینه‌سازی برنامه مدیریت می‌کند.

فعال کردن R8 گامی حیاتی در آماده‌سازی برنامه برای انتشار است، اما توسعه‌دهندگان باید راهنمایی‌هایی در قالب «قوانین حفظ» ارائه دهند.

پس‌از خواندن این مقاله، ویدیو «هفته برجسته عملکرد» را درباره فعال‌سازی، اشکال‌زدایی، و عیب‌یابی بهینه‌ساز R8 در YouTube ببینید.

 

 

چرا به «قوانین نگهداری» نیاز است

نیاز به نوشتن «قوانین حفظ» از یک تضاد اصلی ناشی می‌شود: R8 یک ابزار تحلیل ایستا است، اما برنامه‌های Android اغلب به الگوهای اجرای پویا مانند بازتاب یا فراخوانی‌های داخل و خارج از کد بومی بااستفاده از JNI (میانای بومی Java) متکی هستند.

R8 با تجزیه‌وتحلیل تماس‌های مستقیم، نموداری از کد استفاده‌شده می‌سازد. وقتی به کد به‌صورت پویا دسترسی پیدا می‌شود، تجزیه‌وتحلیل ایستا R8 نمی‌تواند آن را پیش‌بینی کند و آن کد را به‌عنوان استفاده‌نشده شناسایی می‌کند و آن را حذف می‌کند، که منجر به ازکارافتادن زمان اجرا می‌شود.

قانون حفظ دستورالعمل صریحی برای کامپایلر R8 است که بیان می‌کند: «این کلاس، روش، یا فیلد خاص یک نقطه ورودی است که در زمان اجرا به‌صورت پویا قابل‌دسترسی خواهد بود. باید آن را حفظ کنید، حتی اگر نتوانید ارجاع مستقیمی به آن پیدا کنید.»

برای جزئیات بیشتر درباره قوانین Keep، راهنمای رسمی را ببینید.

کجا باید «قوانین نگهداری» را نوشت

«قوانین نگهداری سفارشی» برای برنامه در فایل نوشتاری نوشته می‌شود. طبق قرارداد، این فایل proguard-rules.pro نام دارد و در ریشه برنامه یا واحد کتابخانه قرار دارد. سپس این فایل در نوع ساخت release فایل build.gradle.kts واحد شما مشخص می‌شود.

release {

    isShrinkResources = true

    isMinifyEnabled = true

    proguardFiles(

        getDefaultProguardFile("proguard-android-optimize.txt"),

        "proguard-rules.pro",

    )

}

استفاده از فایل پیش‌فرض صحیح

روش getDefaultProguardFile مجموعه پیش‌فرضی از قوانین ارائه‌شده توسط کیت توسعه نرم‌افزار Android را وارد می‌کند. وقتی از فایل اشتباه استفاده می‌کنید، ممکن است برنامه‌تان بهینه‌سازی نشود. حتماً از proguard-android-optimize.txt استفاده کنید. این فایل «قوانین حفظ» پیش‌فرض را برای مؤلفه‌های استاندارد Android ارائه می‌دهد و بهینه‌سازی‌های کد R8 را فعال می‌کند. proguard-android.txt قدیمی فقط «قوانین Keep» را ارائه می‌دهد اما بهینه‌سازی‌های R8 را فعال نمی‌کند.

progaurd.png

ازآنجایی‌که این مشکل عملکردی جدی است، از Android Studio Narwhal 3 Feature Drop شروع به هشدار دادن به توسعه‌دهندگان درباره استفاده از فایل اشتباه کرده‌ایم. و از نسخه ۹.۰ افزایه Android Gradle، دیگر از فایل قدیمی proguard-android.txt پشتیبانی نمی‌کنیم. بنابراین حتماً به نسخه بهینه‌سازی‌شده ارتقا دهید.

نحوه نوشتن قوانین Keep

قانون نگهداری از سه بخش اصلی تشکیل شده است:

  1. گزینه‌ای مثل -keep یا -keepclassmembers
  2. اصلاح‌گرهای اختیاری مثل allowshrinking
  3. مشخصات کلاس که کد را برای مطابقت تعریف می‌کند

برای مشاهده نحو کامل و مثال‌ها، به راهنمای افزودن قوانین نگهداری مراجعه کنید.

الگوهای منفی قانون Keep

مهم است که درباره روال‌های مطلوب و همچنین الگوهای نامطلوب بدانید. این الگوهای منفی اغلب به‌دلیل سوءتفاهم یا میان‌برهای عیب‌یابی ایجاد می‌شوند و می‌توانند برای عملکرد ساخت تولید فاجعه‌بار باشند.

گزینه‌های سراسری

این پرچم‌ها کلیدهای سراسری هستند که هرگز نباید در ساخت نسخهٔ پخش استفاده شوند. این گزارش‌ها فقط برای اشکال‌زدایی موقت برای جداسازی مشکل است.

استفاده از -dontotptimize به‌طور مؤثری بهینه‌سازی‌های عملکرد R8 را غیرفعال می‌کند و منجر به کندتر شدن برنامه می‌شود.

هنگام استفاده از -dontobfuscate، همه تغییر نام‌ها را غیرفعال می‌کنید و استفاده از -dontshrink حذف کد مرده را خاموش می‌کند. هر دو این قوانین جهانی اندازه برنامه را افزایش می‌دهند.

برای داشتن تجربه کاربری بهتر در برنامه، تا حد امکان از استفاده از این پرچم‌های سراسری در محیط تولید اجتناب کنید.

قوانین نگهداری بیش‌ازحد گسترده

ساده‌ترین راه برای باطل کردن مزایای R8، نوشتن «قوانین حفظ» بسیار گسترده است. قوانین Keep مانند قانون زیر به بهینه‌ساز R8 دستور می‌دهند که هیچ کلاسی را در این بسته یا هیچ‌یک از زیربسته‌های آن کوچک، مبهم، و بهینه‌سازی نکند. این کار مزایای R8 را برای کل آن بسته به‌طور کامل برمی‌دارد. به‌جای آن، سعی کنید «قوانین حفظ» را محدود و خاص بنویسید.
 

-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMS

عملگر وارونگی (!)

عملگر وارونگی (!) روشی قدرتمند برای مستثنی کردن بسته از قانون به‌نظر می‌رسد. اما به این سادگی نیست. این مثال را درنظر بگیرید:

-keep class !com.example.my_package.** { *; } // USE WITH CAUTION

ممکن است فکر کنید این قانون به‌معنای «کلاس‌ها را در com.example.package نگه ندارید» است. اما درواقع به‌معنای «همه کلاس‌ها، روش‌ها، و ویژگی‌ها را در کل برنامه که در com.example.package نیستند نگه دارید» است. اگر این موضوع برایتان عجیب است، بهتر است هرگونه نفی را در پیکربندی R8 خود بررسی کنید.

قوانین زائد برای مؤلفه‌های Android

اشتباه رایج دیگر این است که به‌طور دستی «قوانین حفظ» را برای Activities، Services، یا BroadcastReceivers برنامه‌تان اضافه کنید. این غیرضروری است. فایل پیش‌فرض proguard-android-optimize.txt ازقبل شامل قوانین مربوطه برای این مؤلفه‌های استاندارد Android است تا به‌طور پیش‌فرض کار کنند.

همچنین بسیاری‌از کتابخانه‌ها «قوانین نگهداری» خودشان را دارند. بنابراین نباید لازم باشد قوانین خودتان را برای این موارد بنویسید. اگر مشکلی در Keep Rules از کتابخانه‌ای که استفاده می‌کنید وجود دارد، بهتر است با نویسنده کتابخانه تماس بگیرید تا ببینید مشکل چیست.

روال‌های مطلوب قانون Keep

اکنون که می‌دانید چه کارهایی را نباید انجام دهید، بیایید درباره روال‌های مطلوب صحبت کنیم.

نوشتن «قوانین نگهداری» محدود

«قوانین نگهداری» خوب باید تا حد امکان محدود و مشخص باشد. آن‌ها باید فقط آنچه را که ضروری است حفظ کنند و به R8 اجازه دهند تا بقیه موارد را بهینه‌سازی کند.
 

قانونکیفیت

 

-keep class com.example.** { ; }

 

کم: کل بسته و زیربسته‌های آن را نگه می‌دارد

 

-keep class com.example.MyClass { ; }

 

کم: کل کلاس را نگه می‌دارد که احتمالاً هنوز خیلی گسترده است
-keepclassmembers class com.example.MyClass {

    private java.lang.String secretMessage;

    public void onNativeEvent(java.lang.String);

}
بالا: فقط روش‌ها و ویژگی‌های مرتبط از یک کلاس خاص حفظ می‌شوند

استفاده از اجداد مشترک

به‌جای نوشتن «قوانین حفظ» جداگانه برای چندین مدل داده مختلف، یک قانون بنویسید که کلاس یا میانای پایه مشترکی را هدف قرار دهد. قانون زیر به R8 می‌گوید که هر عضوی از کلاس‌هایی را که این میانای بسیار مقیاس‌پذیر را پیاده‌سازی می‌کنند حفظ کند.

# Keep all fields of any class that implements SerializableModel

-keepclassmembers class * implements com.example.models.SerializableModel {

    <fields>;

}

استفاده از «حاشیه‌نویسی» برای هدف‌یابی چند کلاس

حاشیه‌نویسی سفارشی ایجاد کنید (مثلاً @Serialize) و از آن برای «برچسب‌گذاری» کلاس‌هایی که باید فیلدهایشان حفظ شود استفاده کنید. این الگوی تمیز، اعلانی، و بسیار مقیاس‌پذیر دیگری است. می‌توانید «قوانین حفظ» را برای حاشیه‌نویسی‌های موجود از چارچوب‌هایی که استفاده می‌کنید نیز ایجاد کنید.

# Keep all fields of any class annotated with @Serialize

-keepclassmembers class * {

    @com.example.annotations.Serialize <fields>;

}

انتخاب «گزینه حفظ» مناسب

«گزینه حفظ» مهم‌ترین بخش قانون است. انتخاب گزینه اشتباه می‌تواند بهینه‌سازی را بی‌دلیل غیرفعال کند.

گزینه Keepکارکرد
-keepاز حذف یا تغییر نام کلاس و اعضای ذکرشده در اظهارنامه جلوگیری می‌کند.
-keepclassmembersاز برداشته شدن یا تغییر نام اعضای مشخص‌شده جلوگیری می‌کند، اما اجازه می‌دهد خود کلاس برداشته شود، ولی فقط در کلاس‌هایی که به روش دیگری برداشته نشده‌اند.
-keepclasseswithmembersترکیبی: کلاس و اعضای آن را حفظ می‌کند، فقط درصورتی‌که همه اعضای مشخص‌شده حضور داشته باشند.

می‌توانید اطلاعات بیشتری درباره گزینه حفظ در اسناد مربوط به «گزینه‌های حفظ» ما پیدا کنید.

اجازه دادن به بهینه‌سازی با «اصلاح‌کننده‌ها»

اصلاح‌کننده‌هایی مثل allowshrinking و allowobfuscation قانون گسترده -keep را آسان‌تر می‌کنند و قدرت بهینه‌سازی را به R8 برمی‌گردانند. برای مثال، اگر یک کتابخانه قدیمی شما را مجبور می‌کند از -keep در کل کلاس استفاده کنید، ممکن است بتوانید با مجاز کردن کوچک‌سازی و مبهم‌سازی، بخشی از بهینه‌سازی را بازیابی کنید:

# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it.

-keep,allowshrinking,allowobfuscation class com.example.LegacyClass

افزودن گزینه‌های سراسری برای بهینه‌سازی بیشتر

علاوه‌بر «قوانین حفظ»، می‌توانید پرچم‌های سراسری را به فایل پیکربندی R8 خود اضافه کنید تا بهینه‌سازی بیشتری را تشویق کنید.

-repackageclasses گزینه قدرتمندی است که به R8 دستور می‌دهد همه کلاس‌های مبهم‌شده را به یک بسته منتقل کند. این کار با برداشتن رشته‌های نام بسته زائد، فضای قابل‌توجهی را در فایل DEX ذخیره می‌کند.

-allowaccessmodification به R8 اجازه می‌دهد دسترسی را گسترش دهد (مثلاً private به public) تا درون‌خطی‌سازی تهاجمی‌تری فعال شود. این ویژگی اکنون به‌طور پیش‌فرض هنگام استفاده از proguard-android-optimize.txt فعال است.

هشدار: نویسندگان کتابخانه هرگز نباید این پرچم‌های بهینه‌سازی سراسری را به قوانین مصرف‌کننده خود اضافه کنند، زیرا این پرچم‌ها به‌طور اجباری بر کل برنامه اعمال خواهند شد.

و برای اینکه موضوع واضح‌تر شود، در نسخه ۹.۰ از «افزایه Android Gradle» قصد داریم پرچم‌های بهینه‌سازی سراسری را از کتابخانه‌ها به‌طور کامل نادیده بگیریم. 

روال‌های مطلوب برای کتابخانه‌ها

همه برنامه‌های Android به‌نحوی به کتابخانه‌ها متکی هستند. پس بیایید درباره روال‌های مطلوب برای کتابخانه‌ها صحبت کنیم.

برای توسعه‌دهندگان کتابخانه

اگر کتابخانه‌تان از بازتاب یا JNI استفاده می‌کند، مسئولیت ارائه «قوانین حفظ» لازم به مصرف‌کنندگان آن برعهده شما است. این قوانین در فایل consumer-rules.pro قرار می‌گیرند که سپس به‌طور خودکار در فایل AAR کتابخانه گنجانده می‌شود.

android {

    defaultConfig {

        consumerProguardFiles("consumer-rules.pro")

    }

    ...

}

برای مصرف‌کنندگان کتابخانه

فیلتر کردن «قوانین نگهداری» مشکل‌ساز

اگر باید از کتابخانه‌ای استفاده کنید که شامل «قوانین حفظ» مشکل‌ساز است، می‌توانید آن‌ها را در فایل build.gradle.kts خود که با AGP 9.0 شروع می‌شود فیلتر کنید. این کار به R8 می‌گوید قوانینی را که از وابستگی خاصی می‌آیند نادیده بگیرد.

release {

    optimization.keepRules {

        // Ignore all consumer rules from this specific library

        it.ignoreFrom("com.somelibrary:somelibrary")

    }

}

بهترین قانون Keep این است که قانون Keep نداشته باشید

استراتژی نهایی پیکربندی R8 این است که نیاز به نوشتن «قوانین حفظ» را به‌طور کامل حذف کنید. برای بسیاری‌از برنامه‌ها، این کار را می‌توان با انتخاب کتابخانه‌های مدرنی انجام داد که تولید کد را به بازتاب ترجیح می‌دهند. با تولید کد، بهینه‌ساز می‌تواند راحت‌تر تشخیص دهد که چه کدی واقعاً در زمان اجرا استفاده می‌شود و چه کدی را می‌توان حذف کرد. همچنین عدم استفاده از بازتاب پویا به این معنی است که هیچ نقطه ورودی «پنهانی» وجود ندارد و بنابراین، به «قوانین حفظ» نیازی نیست. هنگام انتخاب کتابخانه جدید، همیشه راه‌حلی را ترجیح دهید که از تولید کد به‌جای بازتاب استفاده می‌کند.

برای اطلاعات بیشتر درباره نحوه انتخاب کتابخانه‌ها، انتخاب هوشمندانه کتابخانه را بررسی کنید.

اشکال‌زدایی و عیب‌یابی پیکربندی R8

وقتی R8 کدی را که باید نگه می‌داشت برمی‌دارد، یا APK شما بزرگ‌تر از حد انتظار است، از این ابزارها برای تشخیص مشکل استفاده کنید.

پیدا کردن «قوانین حفظ» تکراری و سراسری

چون R8 قوانین را از ده‌ها منبع ادغام می‌کند، دانستن اینکه مجموعه قوانین «نهایی» چیست می‌تواند دشوار باشد. افزودن این پرچم به فایل proguard-rules.pro شما گزارش کاملی تولید می‌کند:

# Outputs the final, merged set of rules to the specified file

-printconfiguration build/outputs/logs/configuration.txt

می‌توانید این فایل را جستجو کنید تا قوانین زائد را پیدا کنید یا قانون مشکل‌ساز (مثل -dontoptimize) را به کتابخانه خاصی که آن را دربردارد ردیابی کنید.

Ask R8: Why are you keeping this?

اگر کلاسی که انتظار داشتید برداشته شود هنوز در برنامه‌تان وجود دارد، R8 می‌تواند دلیل آن را به شما بگوید. فقط این قانون را اضافه کنید:

# Asks R8 to explain why it's keeping a specific class

class com.example.MyUnusedClass

-whyareyoukeeping 

درطول ساخت، R8 زنجیره دقیق ارجاعاتی را که باعث شده است آن کلاس را نگه دارد چاپ می‌کند و به شما امکان می‌دهد ارجاع را ردیابی کنید و قوانینتان را اصلاح کنید.

برای راهنمای کامل، بخش عیب‌یابی R8 را بررسی کنید.

مراحل بعدی

‫R8 ابزاری قدرتمند برای بهبود عملکرد برنامه Android است. اثربخشی آن به درک صحیح از عملکرد آن به‌عنوان موتور تحلیل ایستا بستگی دارد.

با نوشتن قوانین خاص در سطح عضو، استفاده از اجداد و حاشیه‌نویسی‌ها، و انتخاب دقیق گزینه‌های نگهداری مناسب، می‌توانید دقیقاً آنچه را که لازم است حفظ کنید. پیشرفته‌ترین روش این است که با انتخاب کتابخانه‌های مدرن مبتنی بر تولید کد به‌جای کتابخانه‌های قدیمی مبتنی بر بازتاب، نیاز به قوانین را به‌طور کامل ازبین ببرید.

همان‌طور که «هفته برجسته عملکرد» را دنبال می‌کنید، حتماً ویدیو «هفته برجسته» امروز را در YouTube ببینید و چالش R8 ما را ادامه دهید. برای هرگونه سؤال درباره فعال‌سازی یا عیب‌یابی R8، از optimizationEnabled #استفاده کنید. ما اینجاییم تا به شما کمک کنیم.

وقت آن است که مزایا را خودتان ببینید.

از شما می‌خواهیم که حالت کامل R8 را برای برنامه‌تان امروز فعال کنید.

  1. برای شروع، راهنماهای توسعه‌دهنده ما را دنبال کنید: فعال کردن بهینه‌سازی برنامه.
  2. بررسی کنید که آیا هنوز از proguard-android.txt استفاده می‌کنید و آن را با proguard-android-optimize.txt جایگزین کنید.
  3. سپس، تأثیر را بسنجید. فقط تفاوت را احساس نکنید، آن را درستی‌سنجی کنید. با اقتباس کردن کد از  برنامه نمونه Macrobenchmark در GitHub، بهبود عملکردتان را بسنجید تا زمان‌های راه‌اندازی را قبل و بعداز آن اندازه‌گیری کنید.

مطمئنیم که بهبود قابل‌توجهی در عملکرد برنامه‌تان خواهید دید.

در این مدت، از برچسب اجتماعی AskAndroid# برای مطرح کردن سؤالاتتان استفاده کنید. درطول هفته، کارشناسان ما سؤالات شما را پایش می‌کنند و به آن‌ها پاسخ می‌دهند.

منتظر فردا باشید، جایی که درباره «بهینه‌سازی با راهنمایی نمایه» با Baseline Profiles و Startup Profiles صحبت خواهیم کرد، نحوه بهبود عملکرد رندر Compose در نسخه‌های گذشته را هم‌رسانی خواهیم کرد، و ملاحظات عملکردی برای کار پس‌زمینه را هم‌رسانی خواهیم کرد.

نویسنده:
ادامه خواندن