其他規則類型

除了保留規則外,您還可以在 R8 中新增影響應用程式最佳化的規則。在您維護保留規則的 proguard-rules.pro 檔案中新增這些規則。

規則可分為以下幾類:

  • 假設
    • -assumevalues
    • -assumenosideeffects
  • 其他最佳化措施
    • -convertchecknotnull
    • -maximumremovedandroidloglevel

假設

這些規則會告知 R8,在執行階段,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,DebugLogger 類別中名為 log 的所有 public static 方法都不應有副作用,這樣 R8 就能移除對這些方法的呼叫。

-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()。如果 barnullbar.getClass() 會擲回 NullPointerException,達到類似的空值檢查效果,但效率更高。

-maximumremovedandroidloglevel

這類規則會移除特定記錄層級或以下層級的 Android 記錄陳述式 (例如 Log.w(...) 和 Log.isLoggable(...))。

maximumremovedandroidloglevel 的語法如下:

-maximumremovedandroidloglevel <log_level> [<class_specification>]

如果您未提供選用的 class_specification,R8 會對整個應用程式套用記錄移除功能。

記錄層級如下:

記錄標籤

記錄層級

詳細程度

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。