Jenis aturan tambahan

R8 memungkinkan Anda menambahkan aturan yang memengaruhi pengoptimalan aplikasi, selain aturan keep. Tambahkan aturan ini dalam file proguard-rules.pro yang sama dengan tempat Anda menyimpan aturan penyimpanan.

Aturan ini dikelompokkan ke dalam kategori berikut:

  • Asumsi
    • -assumevalues
    • -assumenosideeffects
  • Pengoptimalan lainnya
    • -convertchecknotnull
    • -maximumremovedandroidloglevel

Asumsi

Aturan ini memberi tahu R8 bahwa R8 dapat membuat asumsi tertentu tentang perilaku kode tertentu saat runtime.

-assumevalues

Aturan -assumevalues memberi tahu R8 bahwa nilai kolom, atau nilai yang ditampilkan metode, selalu berupa konstanta tertentu atau berada dalam rentang yang ditentukan saat runtime. -assumevalues ditujukan untuk hal-hal seperti nilai tanda yang pada waktu build diketahui memiliki nilai tertentu pada waktu runtime.

Analisis statis standar R8 mungkin tidak dapat menentukan nilai runtime anggota. Dengan -assumevalues, Anda memberi tahu R8 untuk mengasumsikan nilai atau rentang yang ditentukan saat mengoptimalkan kode. Dengan demikian, R8 dapat melakukan pengoptimalan agresif.

Sintaksis untuk -assumevalues mirip dengan sintaksis untuk mempertahankan member_specification, tetapi juga menyertakan return clause sebagai berikut:

<member_specification> return <value> | <range>

Argumen <value> dan <range> mendukung nilai dan jenis berikut:

  • Nilai khusus: true, false, null, @NonNull
  • Nilai primitif: int
  • Referensi kolom statis (termasuk kolom enum)

Untuk menentukan rentang, gunakan format min..max inklusif. Misalnya, cuplikan berikut menunjukkan bahwa variabel CUSTOM_VAL menerima 26 hingga 2147483647:

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

Anda dapat menggunakan aturan ini dalam situasi berikut:

  • Untuk library: Untuk memastikan bahwa saat aplikasi dioptimalkan, semua hook penelusuran bug lokal dihapus dari kode library publik.
  • Untuk aplikasi: Untuk menghapus hal-hal seperti kode debug dari aplikasi rilis. Sebaiknya gunakan varian build dan varian set sumber atau konstanta tertentu, tetapi jika set sumber varian tidak berfungsi untuk kasus Anda, atau jika Anda memerlukan jaminan yang lebih kuat bahwa jalur kode telah sepenuhnya dihapus, gunakan -assumevalues.

Contoh berikut menunjukkan class tempat R8 menghapus alat debug dari versi aplikasi yang dioptimalkan:

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();
    }
}

Aturan berikut menunjukkan cara memberi tahu R8 bahwa variabel IS_OPTIMIZED_VERSION selalu diharapkan ditetapkan ke true.

-assumevalues class com.example.MyConfig {
    public static final boolean IS_OPTIMIZED_VERSION return true;
}

-assumenosideeffects

Aturan -assumenosideeffects memberi tahu R8 bahwa R8 dapat mengasumsikan bahwa anggota yang ditentukan tidak memiliki efek samping. R8 dapat sepenuhnya menghapus panggilan ke metode tersebut yang tidak memiliki nilai yang ditampilkan atau yang menampilkan nilai tetap.

Sintaksis untuk -assumenosideeffects mirip dengan sintaksis untuk mempertahankan member_specification.

Contoh berikut menunjukkan cara memberi tahu R8 bahwa semua metode public static yang bernama log dalam class DebugLogger tidak boleh memiliki efek samping, sehingga R8 dapat menghapus panggilan ke metode ini.

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

Pengoptimalan lainnya

Berikut beberapa pengoptimalan lanjutan lainnya yang tidak diaktifkan secara default. Saat mengaktifkannya, Anda mengizinkan R8 mengoptimalkan kode seperti yang diinstruksikan, selain pengoptimalan default.

-convertchecknotnull

Anda dapat menggunakan aturan -convertchecknotnull untuk mengoptimalkan pemeriksaan null. Hal ini berlaku untuk metode apa pun yang menggunakan parameter objek dan akan memunculkan pengecualian jika objeknya null, mirip dengan pernyataan Kotlin standar. Jenis dan pesan pengecualian tidak harus sama, tetapi perilaku error bersyaratnya sama.

Jika aturan -convertchecknotnull cocok dengan metode tertentu, setiap panggilan ke metode tersebut diganti dengan panggilan ke getClass() pada argumen pertama. Panggilan ke getClass() berfungsi sebagai pengganti pemeriksaan null dan memungkinkan R8 menghapus argumen tambahan dari pemeriksaan null asli, seperti alokasi string yang mahal.

Sintaksis untuk -convertchecknotnull adalah sebagai berikut:

-convertchecknotnull <class_specification> {
   <member_specification>;
}

Misalnya, jika Anda memiliki class Preconditions dengan metode checkNotNull sebagai berikut:

class Preconditions {
    fun <T> checkNotNull(value: T?): T {
        if (value == null) {
            throw NullPointerException()
        } else {
            return value
        }
    }
}

Gunakan aturan berikut:

-convertchecknotnull class com.example.package.Preconditions {
  void checkNotNull(java.lang.Object);
}

Aturan ini mengonversi semua panggilan ke checkNotNull() menjadi panggilan ke getClass pada argumen pertama. Dalam contoh ini, panggilan ke checkNotNull(bar) diganti dengan bar.getClass(). Jika bar adalah null, bar.getClass() akan memunculkan NullPointerException, sehingga mencapai efek pemeriksaan null yang serupa, tetapi lebih efisien.

-maximumremovedandroidloglevel

Jenis aturan ini menghapus pernyataan logging Android (seperti Log.w(...) dan Log.isLoggable(...)) pada atau di bawah tingkat log tertentu.

Sintaksis untuk maximumremovedandroidloglevel adalah sebagai berikut:

-maximumremovedandroidloglevel <log_level> [<class_specification>]

Jika Anda tidak memberikan class_specification opsional, R8 akan menerapkan penghapusan log ke seluruh aplikasi.

Level log adalah sebagai berikut:

Label log

Tingkat log

VERBOSE

2

DEBUG

3

INFO

4

PERINGATAN

5

ERROR

6

ASSERT

7

Misalnya, jika Anda memiliki kode berikut:

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");
  }
}

Dengan aturan berikut:

# A level of 5 corresponds to a log level of WARNING.
-maximumremovedandroidloglevel 5 class Foo { void logSomething(); }

Kode yang dioptimalkan adalah sebagai berikut:

class Foo {
  private static final String TAG = "Foo";
  void logSomething() {
    Log.e(TAG, "Will be logged");
  }
}

Jika Anda memberikan beberapa level log maksimum untuk metode yang sama, R8 akan menggunakan level minimum. Misalnya, dengan aturan berikut:

-maximumremovedandroidloglevel 7 class ** { void foo(); }
-maximumremovedandroidloglevel 4 class ** { void foo(); }

maka tingkat log yang dihapus maksimum untuk foo() adalah 4.