اخبار محصول

پیکربندی و عیب‌یابی قوانین R8 Keep

۷ دقیقه مطالعه
Ajesh Pai و Ben Weiss

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

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

بعد از خواندن این مقاله، ویدیوی Performance Spotlight Week در مورد فعال‌سازی، اشکال‌زدایی و عیب‌یابی بهینه‌ساز R8 را در یوتیوب ببینید.

چرا Keep Rules مورد نیاز است؟

نیاز به نوشتن Keep Rules از یک تضاد اساسی ناشی می‌شود: R8 یک ابزار تحلیل ایستا است، اما برنامه‌های اندروید اغلب به الگوهای اجرای پویا مانند reflection یا فراخوانی‌های ورودی و خروجی کد بومی با استفاده از JNI (رابط بومی جاوا) متکی هستند.

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

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

برای جزئیات بیشتر در مورد Keep Rules به راهنمای رسمی مراجعه کنید.

کجا می‌توان Keep Rules را نوشت؟

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

  release {

    isShrinkResources = true

    isMinifyEnabled = true

    proguardFiles(

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

        "proguard-rules.pro",

    )

}

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

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

پروگارد.png

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

نحوه نوشتن Keep Rules

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

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

برای مشاهده‌ی سینتکس کامل و مثال‌ها، به راهنمای افزودن Keep Rules مراجعه کنید.

ضد الگوهای Keep Rule

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

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

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

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

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

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

قوانین بیش از حد کلی برای عبور از موانع

ساده‌ترین راه برای بی‌اثر کردن مزایای R8 ، نوشتن قوانین Keep بیش از حد کلی است. قوانین Keep مانند نمونه زیر به بهینه‌ساز 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 خود را بررسی کنید.

قوانین زائد برای کامپوننت‌های اندروید

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

همچنین بسیاری از کتابخانه‌ها قوانین Keep مخصوص به خود را دارند. بنابراین لازم نیست قوانین خودتان را برای آنها بنویسید. در صورتی که مشکلی با قوانین 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);

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

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

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

  # Keep all fields of any class that implements SerializableModel

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

    <fields>;

}

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

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

  # Keep all fields of any class annotated with @Serialize

-keepclassmembers class * {

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

}

گزینه Keep مناسب را انتخاب کنید

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

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

می‌توانید اطلاعات بیشتر در مورد گزینه keep را در مستندات ما برای Keep Options بیابید.

امکان بهینه‌سازی با اصلاح‌کننده‌ها

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

  # 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

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

فراتر از Keep Rules، می‌توانید پرچم‌های سراسری را به فایل پیکربندی R8 خود اضافه کنید تا بهینه‌سازی بیشتری را تشویق کنید.

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

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

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

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

بهترین شیوه‌ها برای کتابخانه‌ها

هر برنامه اندروید به نحوی به کتابخانه‌ها متکی است. بنابراین بیایید در مورد بهترین شیوه‌ها برای کتابخانه‌ها صحبت کنیم.

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

اگر کتابخانه شما از reflection یا JNI استفاده می‌کند، شما مسئولیت دارید که Keep Rules های لازم را در اختیار مصرف‌کنندگان آن قرار دهید. این قوانین در فایل consumer-rules.pro قرار می‌گیرند که سپس به طور خودکار در داخل فایل AAR کتابخانه قرار می‌گیرد.

  android {

    defaultConfig {

        consumerProguardFiles("consumer-rules.pro")

    }

    ...

}

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

فیلتر کردن قوانین Keep مشکل‌ساز

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

  release {

    optimization.keepRules {

        // Ignore all consumer rules from this specific library

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

    }

}

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

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

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

اشکال‌زدایی و عیب‌یابی پیکربندی 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 ابزاری قدرتمند برای افزایش عملکرد برنامه‌های اندروید است. اثربخشی آن به درک صحیح از عملکرد آن به عنوان یک موتور تحلیل استاتیک بستگی دارد.

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

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

وقتشه که خودتون فوایدش رو ببینید.

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

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

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

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

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

    نوشته شده توسط:

    ادامه مطلب