除了保留規則外,您還可以在 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()。如果 bar 是 null,bar.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。