اخبار محصول

پیکربندی و عیب‌یابی کردن «قوانین نگهداری R8»

‫۷ دقیقه خواندن
دیدن نمایه Ajesh Pai دیدن نمایه Ben Weiss
Ajesh Pai و Ben Weiss

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

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

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

 

 

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

نیاز به نوشتن «قوانین نگهداری» از یک تعارض اصلی ناشی می‌شود: 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 قدیمی فقط «قوانین نگهداری» را ارائه می‌دهد اما بهینه‌سازی‌های R8 را فعال نمی‌کند.

progaurd.png

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

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

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

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

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

نگه‌داشتن الگوهای ضدقانون

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

گزینه‌های جهانی

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

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

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

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

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

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

-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» محدود

«قوانین خوب 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);

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

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

به‌جای نوشتن «قوانین Keep» جداگانه برای چندین مدل داده مختلف، قانونی بنویسید که کلاس پایه یا میانای مشترکی را هدف‌یابی کند. قانون زیر به 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")

    }

}

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

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

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

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

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

پیدا کردن «قوانین Keep» تکراری و سراسری

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

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

-printconfiguration build/outputs/logs/configuration.txt

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

از R8 بپرسید: چرا این را نگه می‌دارید؟

اگر کلاسی که انتظار داشتید برداشته شود هنوز در برنامه‌تان است، 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 برای مطرح کردن سؤالات خود استفاده کنید. درطول هفته، کارشناسان ما سؤالات شما را پایش و به آن‌ها پاسخ می‌دهند.

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

نوشته:
ادامه خواندن