أنواع القواعد الإضافية

يتيح لك R8 إضافة قواعد تؤثر في تحسين تطبيقك، وذلك بصرف النظر عن قواعد keep. أضِف هذه القواعد في ملف proguard-rules.pro نفسه الذي تحتفظ فيه بقواعد الاحتفاظ بالبيانات.

تندرج القواعد ضمن الفئات التالية:

  • الافتراضات
    • -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
  • مراجع الحقول الثابتة (بما في ذلك حقول التعداد)

لتحديد نطاق، استخدِم التنسيق الشامل min..max. على سبيل المثال، يوضّح المقتطف التالي أنّ المتغيّر CUSTOM_VAL يقبل القيم من 26 إلى 2147483647:

-assumevalues public class com.example.Foo {
    public static int CUSTOM_VAL return 26..2147483647;
}

يمكنك استخدام هذه القاعدة في الحالات التالية:

  • بالنسبة إلى المكتبات: لضمان إزالة جميع أدوات تصحيح الأخطاء المحلية من الرموز البرمجية للمكتبة العامة عند تحسين التطبيقات
  • بالنسبة إلى التطبيقات: لإزالة عناصر مثل رمز تصحيح الأخطاء من تطبيق إصدار، من الأفضل استخدام تنويعات الإصدار وتنويعات مجموعات رموز المصدر أو الثوابت المحدّدة، ولكن إذا لم تنجح مجموعات رموز المصدر الخاصة بالتنويعات في حالتك، أو إذا كنت بحاجة إلى ضمان أقوى بأنّه تمت إزالة مسارات الرموز بالكامل، استخدِم -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 بأنّه يجب ألا يكون لأي من الطرق التي تحمل الاسم log ضمن الفئة DebugLogger أي آثار جانبية، ما يتيح إزالة استدعاءات هذه الطرق.public static

-assumenosideeffects class com.example.DebugLogger {
    public static void log(...);
}

تحسينات أخرى

في ما يلي بعض التحسينات الأكثر تقدّمًا والتي لا تكون مفعّلة تلقائيًا. عند تفعيل هذه التحسينات، تسمح لأداة R8 بتحسين الرمز البرمجي وفقًا للتعليمات، بالإضافة إلى التحسينات التلقائية.

-convertchecknotnull

يمكنك استخدام القاعدة -convertchecknotnull لتحسين عمليات التحقّق من القيم الفارغة. وينطبق ذلك على أي طريقة تأخذ مَعلمة كائن وتُصدر استثناءً إذا كان الكائن فارغًا، على غرار تأكيد Kotlin العادي. قد يختلف نوع الاستثناء عن الرسالة، ولكن سلوك التعطُّل الشرطي هو نفسه.

إذا كانت قاعدة -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، ما يحقّق تأثيرًا مشابهًا للتحقّق من القيمة الخالية ولكن بشكل أكثر فعالية.

-maximumremovedandroidloglevel

يزيل نوع القاعدة هذا عبارات تسجيل Android (مثل Log.w(...) وLog.isLoggable(...)) عند مستوى سجلّ معيّن أو أقل منه.

صيغة maximumremovedandroidloglevel هي كما يلي:

-maximumremovedandroidloglevel <log_level> [<class_specification>]

في حال عدم توفير class_specification الاختياري، يطبِّق R8 عملية إزالة السجلّ على التطبيق بأكمله.

في ما يلي مستويات السجلّ:

تصنيف السجلّ

مستوى السجلّ

VERBOSE

2

تصحيح الأخطاء

3

معلومات

4

تحذير

5

خطأ

6

ASSERT

7

على سبيل المثال، إذا كانت لديك التعليمة البرمجية التالية:

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() هو 4.