انواع قوانین اضافی

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() برابر با ۴ است.