R8 به شما امکان میدهد قوانینی را اضافه کنید که بر بهینهسازی برنامه شما تأثیر میگذارند، جدا از قوانین keep. این قوانین را در همان فایل proguard-rules.pro که قوانین keep خود را در آن نگهداری میکنید، اضافه کنید.
قوانین در دستههای زیر قرار میگیرند:
- فرضیات
-
-assumevalues -
-assumenosideeffects
-
- بهینهسازیهای دیگر
-
-convertchecknotnull -
-maximumremovedandroidloglevel
-
فرضیات
این قوانین به R8 میگویند که میتواند در زمان اجرا، فرضیات خاصی در مورد رفتار کد خاص داشته باشد.
-assumevalues
قانون -assumevalues به R8 میگوید که مقدار یک فیلد یا مقدار بازگشتی یک متد، همیشه یک ثابت خاص است یا در زمان اجرا در یک محدوده تعریفشده قرار میگیرد. -assumevalues برای مواردی مانند مقادیر پرچم در نظر گرفته شده است که در زمان ساخت، مشخص است که در زمان اجرا مقادیر خاصی دارند.
تحلیل استاتیک استاندارد R8 ممکن است نتواند مقادیر زمان اجرای اعضا را تعیین کند. با استفاده از -assumevalues به R8 میگویید که هنگام بهینهسازی کد، مقدار یا محدوده مشخصشده را فرض کند. این به R8 اجازه میدهد بهینهسازیهای تهاجمی انجام دهد.
سینتکس -assumevalues مشابه سینتکس مربوط به نگهداری member_specification است، اما علاوه بر آن شامل یک return clause به شرح زیر نیز میباشد:
<member_specification> return <value> | <range>
آرگومانهای <value> و <range> از مقادیر و انواع زیر پشتیبانی میکنند:
- مقادیر ویژه:
true, false, null, @NonNull - مقادیر اولیه:
int - ارجاعات فیلدهای استاتیک (شامل فیلدهای enum)
برای تعریف یک محدوده، از قالب شامل min..max استفاده کنید. برای مثال، قطعه کد زیر نشان میدهد که متغیر CUSTOM_VAL از اعداد ۲۶ تا ۲۱۴۷۴۸۳۶۴۷ را میپذیرد:
-assumevalues public class com.example.Foo {
public static int CUSTOM_VAL return 26..2147483647;
}
شما میتوانید از این قانون در موقعیتهای زیر استفاده کنید:
- برای کتابخانهها : برای اطمینان از اینکه هنگام بهینهسازی برنامهها، تمام قلابهای اشکالزدایی محلی از کد کتابخانه عمومی حذف میشوند.
- برای برنامهها : برای حذف مواردی مانند کد اشکالزدایی از یک برنامهی انتشار. ترجیحاً از build variants و variants از مجموعههای منبع یا ثابتهای خاص استفاده کنید، اما اگر مجموعههای منبع متغیر برای مورد شما کار نمیکنند، یا اگر به تضمین قویتری نیاز دارید که مسیرهای کد به طور کامل حذف شدهاند،
-assumevaluesاستفاده کنید.
مثال زیر کلاسی را نشان میدهد که در آن R8 ابزارهای اشکالزدایی را از نسخه بهینهشده یک برنامه حذف میکند:
package com.example;
public class MyConfig {
// This field is initialized to false but is overwritten by a resource
// value or other mechanism in the final build process. R8's static analysis
// might see the initial 'false' but the runtime value is known to be
// 'true'.
public static final boolean IS_OPTIMIZED_VERSION = false;
}
// In another class:
public void initFeatures() {
if (MyConfig.IS_OPTIMIZED_VERSION) {
System.out.println("Starting optimized features...");
android.util.Log.d(TAG, "Starting optimized features...");
initOptimizedService();
} else {
android.util.Log.d(TAG, "Starting debug/logging features...");
initDebugTools();
}
}
قانون زیر نشان میدهد که چگونه به R8 بگوییم که انتظار میرود متغیر IS_OPTIMIZED_VERSION همیشه روی true تنظیم شود.
-assumevalues class com.example.MyConfig {
public static final boolean IS_OPTIMIZED_VERSION return true;
}
-assumenosideeffects
قانون -assumenosideeffects به R8 میگوید که میتواند فرض کند اعضای مشخصشده هیچ عارضه جانبی ندارند. R8 میتواند فراخوانیهای چنین متدهایی را که هیچ مقدار بازگشتی ندارند یا مقدار ثابتی را برمیگردانند، بهطور کامل حذف کند.
سینتکس -assumenosideeffects مشابه سینتکس مربوط به نگهداری member_specification است.
نمونهی زیر نشان میدهد که چگونه به R8 بگوییم که تمام متدهای public static با نام log در کلاس DebugLogger نباید هیچ عارضهی جانبی داشته باشند، که به آن اجازه میدهد فراخوانیهای این متدها را حذف کند.
-assumenosideeffects class com.example.DebugLogger {
public static void log(...);
}
بهینهسازیهای دیگر
اینها برخی از بهینهسازیهای پیشرفتهتر هستند که به طور پیشفرض فعال نیستند. با فعال کردن آنها، به R8 اجازه میدهید علاوه بر بهینهسازیهای پیشفرض، کد را طبق دستورالعمل بهینه کند.
-convertchecknotnull
شما میتوانید از قانون -convertchecknotnull برای بهینهسازی بررسیهای null استفاده کنید. این قانون در مورد هر متدی که یک پارامتر شیء میگیرد و در صورت null بودن شیء، throws میکند، مشابه یک assertion استاندارد کاتلین، صدق میکند. نوع استثنا و پیام لزوماً یکسان نیستند، اما رفتار crash کردن شرطی یکسان است.
اگر یک قاعده -convertchecknotnull با یک متد مشخص مطابقت داشته باشد، هر فراخوانی آن متد با فراخوانی getClass() در اولین آرگومان جایگزین میشود. فراخوانیهای getClass() به عنوان یک بررسی تهی جایگزین عمل میکنند و به R8 اجازه میدهند هرگونه آرگومان اضافی از بررسی تهی اصلی، مانند تخصیصهای رشتهای پرهزینه، را حذف کند.
سینتکس -convertchecknotnull به شرح زیر است:
-convertchecknotnull <class_specification> {
<member_specification>;
}
برای مثال، اگر کلاس Preconditions با متد checkNotNull به صورت زیر داشته باشید:
class Preconditions {
fun <T> checkNotNull(value: T?): T {
if (value == null) {
throw NullPointerException()
} else {
return value
}
}
}
از قانون زیر استفاده کنید:
-convertchecknotnull class com.example.package.Preconditions {
void checkNotNull(java.lang.Object);
}
این قانون تمام فراخوانیهای تابع checkNotNull() را به فراخوانیهای تابع getClass در اولین آرگومان تبدیل میکند. در این مثال، فراخوانی تابع checkNotNull(bar) با bar.getClass() جایگزین میشود. اگر bar null داشته باشد، bar.getClass() یک NullPointerException ایجاد میکند که نتیجهای مشابه با بررسی null اما با کارایی بیشتر دارد.
-maximumremovedandroidloglevel
این نوع قانون، دستورات ثبت وقایع اندروید (مانند Log.w (...) و Log.isLoggable(...) ) را در سطح یا پایینتر از سطح ثبت وقایع خاصی حذف میکند.
سینتکس تابع maximumremovedandroidloglevel به صورت زیر است:
-maximumremovedandroidloglevel <log_level> [<class_specification>]
اگر class_specification اختیاری را ارائه ندهید، R8 حذف لاگ را برای کل برنامه اعمال میکند.
سطوح ثبت وقایع به شرح زیر است:
برچسب لاگ | سطح لاگ |
پرگو | ۲ |
اشکالزدایی | ۳ |
اطلاعات | ۴ |
هشدار | ۵ |
خطا | ۶ |
ادعا | ۷ |
برای مثال، اگر کد زیر را داشته باشید:
class Foo {
private static final String TAG = "Foo";
void logSomething() {
if (Log.isLoggable(TAG, WARNING)) {
Log.e(TAG, "Won't be logged");
}
Log.w(TAG, "Won't be logged");
Log.e(TAG, "Will be logged");
}
}
با قانون زیر:
# A level of 5 corresponds to a log level of WARNING.
-maximumremovedandroidloglevel 5 class Foo { void logSomething(); }
کد بهینه شده به صورت زیر است:
class Foo {
private static final String TAG = "Foo";
void logSomething() {
Log.e(TAG, "Will be logged");
}
}
اگر چندین سطح حداکثر ثبت وقایع را برای یک متد مشخص کنید، R8 از حداقل سطح استفاده میکند. برای مثال، با توجه به قوانین زیر:
-maximumremovedandroidloglevel 7 class ** { void foo(); }
-maximumremovedandroidloglevel 4 class ** { void foo(); }
سپس حداکثر سطح لاگ حذف شده برای foo() برابر با ۴ است.