Menambahkan aturan simpan

Secara umum, aturan keep menentukan class (atau subclass atau implementasi), lalu anggota—metode, konstruktor, atau kolom—dalam class tersebut yang akan dipertahankan.

Sintaksis umum untuk aturan penyimpanan adalah sebagai berikut:


-<keep_option>[,<keep_option_modifier_1>,<keep_option_modifier_2>,...] <class_specification>

Berikut adalah contoh aturan penyimpanan yang menggunakan keepclassmembers sebagai opsi penyimpanan, allowoptimization sebagai pengubah, dan menyimpan someSpecificMethod() dari com.example.MyClass:

-keepclassmembers,allowoptimization class com.example.MyClass {
  void someSpecificMethod();
}

Opsi pertahankan

Opsi keep adalah bagian pertama dari aturan penyimpanan Anda. Menentukan aspek class yang akan dipertahankan. Ada enam opsi penyimpanan yang berbeda, yaitu keep, keepclassmembers, keepclasseswithmembers, keepnames, keepclassmembernames, keepclasseswithmembernames.

Tabel berikut menjelaskan opsi penyimpanan ini:

Opsi Keep Deskripsi
keepclassmembers Mempertahankan anggota yang ditentukan hanya jika R8 tidak menghapus class yang memuatnya.
keep Mempertahankan class dan anggota (kolom dan metode) yang ditentukan, sehingga mencegahnya dioptimalkan.

Catatan: keep umumnya hanya boleh digunakan dengan pengubah opsi tetap karena keep dengan sendirinya mencegah pengoptimalan apa pun terjadi pada class yang cocok.
keepclasseswithmembers Mempertahankan class dan anggota yang ditentukan hanya jika class memiliki semua anggota dari spesifikasi class.
keepclassmembernames Mencegah penggantian nama anggota class yang ditentukan, tetapi tidak mencegah penghapusan class atau anggotanya.

Catatan: Arti opsi ini sering disalahpahami; sebaiknya gunakan -keepclassmembers,allowshrinking yang setara.
keepnames Mencegah penggantian nama kelas dan anggotanya, tetapi tidak mencegah penghapusan seluruhnya jika dianggap tidak digunakan.

Catatan: Arti opsi ini sering disalahpahami; sebaiknya gunakan -keep,allowshrinking yang setara.
keepclasseswithmembernames Mencegah penggantian nama kelas dan anggota yang ditentukan, tetapi hanya jika anggota ada dalam kode akhir. Opsi ini tidak mencegah penghapusan kode.

Catatan: Arti opsi ini sering disalahpahami; sebaiknya gunakan -keepclasseswithmembers,allowshrinking yang setara.

Memilih opsi penyimpanan yang tepat

Memilih opsi keep yang tepat sangat penting dalam menentukan pengoptimalan yang tepat untuk aplikasi Anda. Opsi keep tertentu menyusutkan kode, yaitu proses penghapusan kode yang tidak direferensikan, sementara opsi lainnya meng-obfuscate, atau mengganti nama, kode. Tabel berikut menunjukkan tindakan dari berbagai opsi penyimpanan:

Opsi Keep Kelas penyusutan Meng-obfuscate class Mengecilkan anggota Mengaburkan anggota
keep
keepclassmembers
keepclasseswithmembers
keepnames
keepclassmembernames
keepclasseswithmembernames

Pengubah opsi pertahankan

Pengubah opsi penyimpanan digunakan untuk mengontrol cakupan dan perilaku aturan penyimpanan. Anda dapat menambahkan 0 atau lebih pengubah opsi penyimpanan ke aturan penyimpanan.

Kemungkinan nilai untuk pengubah opsi tetap dijelaskan dalam tabel berikut:

Nilai Deskripsi
allowoptimization Mengizinkan pengoptimalan elemen yang ditentukan. Namun, elemen yang ditentukan tidak diganti namanya atau dihapus.
allowobfucastion Mengizinkan penggantian nama elemen yang ditentukan. Namun, elemen tidak akan dihapus atau dioptimalkan.
allowshrinking Memungkinkan penghapusan elemen yang ditentukan jika R8 tidak menemukan referensi ke elemen tersebut. Namun, elemen tidak diganti namanya atau dioptimalkan.
includedescriptorclasses Memerintahkan R8 untuk menyimpan semua class yang muncul dalam deskriptor metode (jenis parameter dan jenis nilai yang ditampilkan) dan kolom (jenis kolom) yang disimpan.
allowaccessmodification Mengizinkan R8 mengubah (biasanya memperluas) pengubah akses (public, private, protected) class, metode, dan kolom selama proses pengoptimalan.
allowrepackage Memungkinkan R8 memindahkan class ke paket yang berbeda, termasuk paket default (root).

Spesifikasi kelas

Anda harus menentukan class (termasuk class antarmuka, enum, dan anotasi) di setiap aturan keep. Secara opsional, Anda dapat membatasi aturan berdasarkan anotasi, dengan menentukan superclass atau antarmuka yang diimplementasikan, atau dengan menentukan pengubah akses untuk class. Semua class, termasuk class dari namespace java.lang seperti java.lang.String, harus ditentukan menggunakan nama Java yang sepenuhnya memenuhi syarat. Untuk memahami nama yang harus digunakan, periksa bytecode menggunakan alat yang dijelaskan dalam Memeriksa nama Java yang dihasilkan.

Contoh berikut menunjukkan cara menentukan class MaterialButton:

  • Benar: com.google.android.material.button.MaterialButton
  • Salah: MaterialButton

Spesifikasi class juga menentukan anggota dalam class yang harus dipertahankan. Misalnya, aturan berikut mempertahankan class MyClass dan metode someSpecificMethod():

-keep class com.example.MyClass {
  void someSpecificMethod();
}

Menentukan kelas berdasarkan anotasi

Untuk menentukan class berdasarkan anotasinya, tambahkan awalan nama Java yang sepenuhnya memenuhi syarat dari anotasi dengan simbol @. Contoh:

-keep class @com.example.MyAnnotation com.example.MyClass

Jika aturan penyimpanan memiliki lebih dari satu anotasi, aturan tersebut akan menyimpan class yang memiliki semua anotasi yang tercantum. Anda dapat mencantumkan beberapa anotasi, tetapi aturan hanya berlaku jika class memiliki setiap anotasi yang tercantum. Misalnya, aturan berikut mempertahankan semua class yang dianotasi oleh Annotation1 dan Annotation2.

-keep class @com.example.Annotation1 @com.example.Annotation2 *

Menentukan subclass dan implementasi

Untuk menargetkan subclass, atau class yang mengimplementasikan antarmuka, gunakan extend dan implements.

Misalnya, jika Anda memiliki class Bar dengan subclass Foo sebagai berikut:

class Foo : Bar()

Aturan keep berikut mempertahankan semua subkelas Bar. Perhatikan bahwa aturan keep tidak menyertakan superclass Bar itu sendiri.

-keep class * extends Bar

Jika Anda memiliki class Foo yang mengimplementasikan antarmuka Bar:

class Foo : Bar

Aturan keep berikut mempertahankan semua class yang menerapkan Bar. Perhatikan bahwa aturan tetap tidak menyertakan antarmuka Bar itu sendiri.

-keep class * implements Bar

Menentukan class berdasarkan pengubah akses

Anda dapat menentukan pengubah akses seperti public, private, static, dan final untuk membuat aturan penyimpanan Anda lebih presisi.

Misalnya, aturan berikut menyimpan semua class public dalam paket api dan sub-paketnya, serta semua anggota publik dan yang dilindungi dalam class ini.

-keep public class com.example.api.** { public protected *; }

Anda juga dapat menggunakan pengubah untuk anggota dalam class. Misalnya, aturan berikut hanya menyimpan metode public static dari class Utils:

-keep class com.example.Utils {
  public static void *(...);
}

Pengubah khusus Kotlin

R8 tidak mendukung pengubah khusus Kotlin seperti internal dan suspend. Gunakan panduan berikut untuk mempertahankan kolom tersebut.

  • Untuk mempertahankan class, metode, atau kolom internal, perlakukan sebagai publik. Misalnya, perhatikan sumber Kotlin berikut:

    package com.example
    internal class ImportantInternalClass {
      internal val f: Int
      internal fun m() {}
    }
    

    Class, metode, dan kolom internal adalah public dalam file .class yang dihasilkan oleh compiler Kotlin, jadi Anda harus menggunakan kata kunci public seperti yang ditunjukkan dalam contoh berikut:

    -keepclassmembers public class com.example.ImportantInternalClass {
      public int f;
      public void m();
    }
    
  • Saat anggota suspend dikompilasi, cocokkan tanda tangan yang dikompilasi dalam aturan keep.

    Misalnya, jika Anda memiliki fungsi fetchUser yang ditentukan seperti yang ditunjukkan dalam cuplikan berikut:

    suspend fun fetchUser(id: String): User
    

    Saat dikompilasi, tanda tangannya dalam bytecode akan terlihat seperti berikut:

    public final Object fetchUser(String id, Continuation<? super User> continuation);
    

    Untuk menulis aturan keep untuk fungsi ini, Anda harus mencocokkan tanda tangan yang dikompilasi ini, atau menggunakan ....

    Contoh penggunaan tanda tangan yang dikompilasi adalah sebagai berikut:

    -keepclassmembers class com.example.repository.UserRepository {
      public java.lang.Object fetchUser(java.lang.String,  kotlin.coroutines.Continuation);
    }
    

    Contoh penggunaan ... adalah sebagai berikut:

    -keepclassmembers class com.example.repository.UserRepository {
      public java.lang.Object fetchUser(...);
    }
    

Spesifikasi anggota

Spesifikasi class secara opsional mencakup anggota class yang akan dipertahankan. Jika Anda menentukan satu atau beberapa anggota untuk suatu kelas, aturan tidak berlaku untuk anggota lain.

Menentukan anggota berdasarkan anotasi

Anda dapat menentukan anggota berdasarkan anotasi mereka. Mirip dengan class, Anda menggunakan awalan @ pada nama Java yang sepenuhnya memenuhi syarat untuk anotasi. Dengan begitu, Anda dapat mempertahankan hanya anggota dalam class yang ditandai dengan anotasi tertentu. Misalnya, untuk mempertahankan metode dan kolom yang dianotasi dengan @com.example.MyAnnotation:

-keep class com.example.MyClass {
  @com.example.MyAnnotation <methods>;
  @com.example.MyAnnotation <fields>;
}

Anda dapat menggabungkannya dengan pencocokan anotasi tingkat class untuk aturan yang efektif dan bertarget:

-keep class @com.example.ClassAnnotation * {
  @com.example.MethodAnnotation <methods>;
  @com.example.FieldAnnotation <fields>;
}

Hal ini mempertahankan class yang diberi anotasi dengan @ClassAnnotation, dan pada class tersebut, hal ini mempertahankan metode yang diberi anotasi oleh @MethodAnnotation dan kolom yang diberi anotasi oleh @FieldAnnotation.

Sebaiknya gunakan aturan penyimpanan berbasis anotasi jika memungkinkan. Pendekatan ini memberikan link eksplisit antara kode dan aturan keep Anda, serta sering kali menghasilkan konfigurasi yang lebih tangguh. Library anotasi androidx.annotation, misalnya, menggunakan mekanisme ini.

Metode

Sintaksis untuk menentukan metode dalam spesifikasi anggota untuk aturan penyimpanan adalah sebagai berikut:

[<access_modifier>] [<return_type>] <method_name>(<parameter_types>);

Misalnya, aturan keep berikut mempertahankan metode publik bernama setLabel() yang menampilkan void dan mengambil String.

-keep class com.example.MyView {
    public void setLabel(java.lang.String);
}

Anda dapat menggunakan <methods> sebagai pintasan untuk mencocokkan semua metode dalam class sebagai berikut:

-keep class com.example.MyView {
    <methods>;
}

Untuk mempelajari lebih lanjut cara menentukan jenis untuk jenis nilai yang ditampilkan dan jenis parameter, lihat Jenis.

Konstruktor

Untuk menentukan konstruktor, gunakan <init>. Sintaksis untuk menentukan konstruktor dalam spesifikasi anggota untuk aturan penyimpanan adalah sebagai berikut:

[<access_modifier>] <init>(parameter_types);

Misalnya, aturan penyimpanan berikut menyimpan konstruktor View kustom yang mengambil Context dan AttributeSet.

-keep class com.example.ui.MyCustomView {
    public <init>(android.content.Context, android.util.AttributeSet);
}

Untuk mempertahankan semua konstruktor publik, gunakan contoh berikut sebagai referensi:

-keep class com.example.ui.MyCustomView {
    public <init>(...);
}

Kolom

Sintaksis untuk menentukan kolom dalam spesifikasi anggota untuk aturan penyimpanan adalah sebagai berikut:

[<access_modifier>...] [<type>] <field_name>;

Misalnya, aturan penyimpanan berikut menyimpan kolom string pribadi bernama userId dan kolom bilangan bulat statis publik bernama STATUS_ACTIVE:

-keep class com.example.models.User {
    private java.lang.String userId;
    public static int STATUS_ACTIVE;
}

Anda dapat menggunakan <fields> sebagai pintasan untuk mencocokkan semua kolom dalam class sebagai berikut:

-keep class com.example.models.User {
    <fields>;
}

Jenis

Bagian ini menjelaskan cara menentukan jenis nilai yang ditampilkan, jenis parameter, dan jenis kolom dalam spesifikasi anggota aturan penyimpanan. Jangan lupa untuk menggunakan nama Java yang dihasilkan untuk menentukan jenis jika berbeda dengan kode sumber Kotlin.

Jenis primitif

Untuk menentukan jenis primitif, gunakan kata kunci Java-nya. R8 mengenali jenis primitif berikut: boolean, byte, short, char, int, long, float, double.

Contoh aturan dengan jenis primitif adalah sebagai berikut:

# Keeps a method that takes an int and a float as parameters.
-keepclassmembers class com.example.Calculator {
    public void setValues(int, float);
}

Jenis generik

Selama kompilasi, compiler Kotlin/Java menghapus informasi jenis generik, jadi saat Anda menulis aturan keep yang melibatkan jenis generik, Anda harus menargetkan representasi kode yang dikompilasi, dan bukan kode sumber asli. Untuk mempelajari lebih lanjut cara mengubah jenis generik, lihat Penghapusan jenis.

Misalnya, jika Anda memiliki kode berikut dengan jenis generik tanpa batas yang ditentukan dalam Box.kt:

package com.myapp.data

class Box<T>(val item: T) {
    fun getItem(): T {
        return item
    }
}

Setelah penghapusan jenis, T diganti dengan Object. Untuk mempertahankan konstruktor dan metode class, aturan Anda harus menggunakan java.lang.Object, bukan T generik.

Contoh aturan penyimpanan adalah sebagai berikut:

# Keep the constructor and methods of the Box class.
-keep class com.myapp.data.Box {
    public init(java.lang.Object);
    public java.lang.Object getItem();
}

Jika Anda memiliki kode berikut dengan jenis generik terikat di NumberBox.kt:

package com.myapp.data

// T is constrained to be a subtype of Number
class NumberBox<T : Number>(val number: T)

Dalam hal ini, penghapusan jenis menggantikan T dengan batasnya, java.lang.Number.

Contoh aturan penyimpanan adalah sebagai berikut:

-keep class com.myapp.data.NumberBox {
    public init(java.lang.Number);
}

Saat menggunakan jenis generik khusus aplikasi sebagai class dasar, Anda juga harus menyertakan aturan penyimpanan untuk class dasar.

Misalnya, untuk kode berikut:

package com.myapp.data

data class UnpackOptions(val useHighPriority: Boolean)

// The generic Box class with UnpackOptions as the bounded type
class Box<T: UnpackOptions>(val item: T) {
}

Anda dapat menggunakan aturan penyimpanan dengan includedescriptorclasses untuk mempertahankan class UnpackOptions dan metode class Box dengan satu aturan sebagai berikut:

-keep,includedescriptorclasses class com.myapp.data.Box {
    public <init>(com.myapp.data.UnpackOptions);
}

Untuk mempertahankan fungsi tertentu yang memproses daftar objek, Anda harus menulis aturan yang cocok dengan tanda tangan fungsi tersebut. Perhatikan bahwa karena jenis generik dihapus, parameter seperti List<Product> akan dilihat sebagai java.util.List.

Misalnya, jika Anda memiliki class utilitas dengan fungsi yang memproses daftar objek Product sebagai berikut:

package com.myapp.utils

import com.myapp.data.Product
import android.util.Log

class DataProcessor {
    // This is the function we want to keep
    fun processProducts(products: List<Product>) {
        Log.d("DataProcessor", "Processing ${products.size} products.")
        // Business logic ...
    }
}

// The data class used in the list (from the previous example)
package com.myapp.data
data class Product(val id: String, val name: String)

Anda dapat menggunakan aturan keep berikut untuk melindungi hanya fungsi processProducts:

-keep class com.myapp.utils.DataProcessor {
    public void processProducts(java.util.List);
}

Jenis array

Tentukan jenis array dengan menambahkan [] ke jenis komponen untuk setiap dimensi array. Hal ini berlaku untuk jenis class dan jenis primitif.

  • Array kelas satu dimensi: java.lang.String[]
  • Array primitif dua dimensi: int[][]

Misalnya, jika Anda memiliki kode berikut:

package com.example.data

class ImageProcessor {
  fun process(): ByteArray {
    // process image to return a byte array
  }
}

Anda dapat menggunakan aturan penyimpanan berikut:

# Keeps a method that returns a byte array.
-keepclassmembers class com.example.data.ImageProcessor {
    public byte[] process();
}

Contoh

Misalnya, untuk mempertahankan class tertentu dan semua anggotanya, gunakan berikut ini:

-keep class com.myapp.MyClass { *; }

Untuk mempertahankan hanya class, beserta konstruktor defaultnya, tetapi bukan anggota lainnya, gunakan kode berikut:

-keep class com.myapp.MyClass

Sebaiknya Anda selalu menentukan beberapa anggota. Misalnya, contoh berikut menyimpan kolom publik text dan metode publik updateText() dalam class MyClass.

-keep class com.myapp.MyClass {
    public java.lang.String text;
    public void updateText(java.lang.String);
}

Untuk mempertahankan semua kolom publik dan metode publik, lihat contoh berikut:

-keep public class com.example.api.ApiClient {
    public *;
}

Menghilangkan spesifikasi anggota

Menghilangkan spesifikasi anggota menyebabkan R8 mempertahankan konstruktor default untuk class.

Misalnya, jika Anda menulis -keep class com.example.MyClass atau -keep class com.example.MyClass {}, R8 akan memperlakukannya seolah-olah Anda menulis berikut:

-keep class com.example.MyClass{
  void <init>();
}

Fungsi tingkat paket

Untuk mereferensikan fungsi Kotlin yang ditentukan di luar class (biasanya disebut fungsi level teratas), pastikan untuk menggunakan nama Java yang dihasilkan untuk class yang ditambahkan secara implisit oleh compiler Kotlin. Nama class adalah nama file Kotlin dengan Kt ditambahkan. Misalnya, jika Anda memiliki file Kotlin bernama MyClass.kt yang ditentukan sebagai berikut:

package com.example.myapp.utils

// A top-level function not inside a class
fun isEmailValid(email: String): Boolean {
    return email.contains("@")
}

Untuk menulis aturan keep untuk fungsi isEmailValid, spesifikasi class harus menargetkan class MyClassKt yang dihasilkan:

-keep class com.example.myapp.utils.MyClassKt {
    public static boolean isEmailValid(java.lang.String);
}

Karakter pengganti

Tabel berikut menunjukkan cara menggunakan karakter pengganti untuk menerapkan aturan penyimpanan ke beberapa class atau anggota yang cocok dengan pola tertentu.

Karakter pengganti Berlaku untuk kelas atau anggota Deskripsi
** Keduanya Paling sering digunakan. Mencocokkan nama jenis apa pun, termasuk sejumlah pemisah paket. Hal ini berguna untuk mencocokkan semua class dalam paket dan sub-paketnya.
* Keduanya Untuk spesifikasi class, cocok dengan bagian mana pun dari nama jenis yang tidak berisi pemisah paket (.)
Untuk spesifikasi anggota, cocok dengan nama metode atau kolom apa pun. Jika digunakan sendiri, nilai ini juga merupakan alias untuk **.
? Keduanya Mencocokkan satu karakter dalam nama kelas atau anggota.
*** Pelanggan Mencocokkan jenis apa pun, termasuk jenis primitif (seperti int), jenis class (seperti java.lang.String), dan jenis array dari dimensi apa pun (seperti byte[][]).
... Pelanggan Mencocokkan daftar parameter untuk suatu metode.
% Pelanggan Mencocokkan jenis primitif apa pun (seperti `int`, `float`, `boolean`, atau lainnya).

Berikut beberapa contoh cara menggunakan karakter pengganti khusus:

  • Jika Anda memiliki beberapa metode dengan nama yang sama yang menggunakan jenis primitif yang berbeda sebagai input, Anda dapat menggunakan % untuk menulis aturan keep yang mempertahankan semuanya. Misalnya, class DataStore ini memiliki beberapa metode setValue:

    class DataStore {
        fun setValue(key: String, value: Int) { ... }
        fun setValue(key: String, value: Boolean) { ... }
        fun setValue(key: String, value: Float) { ... }
    }
    

    Aturan keep berikut akan mempertahankan semua metode:

    -keep class com.example.DataStore {
        public void setValue(java.lang.String, %);
    }
    
  • Jika Anda memiliki beberapa class dengan nama yang berbeda satu karakter, gunakan ? untuk menulis aturan penyimpanan yang menyimpan semuanya. Misalnya, jika Anda memiliki class berikut:

    com.example.models.UserV1 {...}
    com.example.models.UserV2 {...}
    com.example.models.UserV3 {...}
    

    Aturan penyimpanan berikut akan menyimpan semua class:

    -keep class com.example.models.UserV?
    
  • Untuk mencocokkan class Example dan AnotherExample (jika merupakan class tingkat root), tetapi tidak com.foo.Example, gunakan aturan keep berikut:

    -keep class *Example
    
  • Jika Anda menggunakan * dengan sendirinya, * akan bertindak sebagai alias untuk **. Misalnya, aturan penyimpanan berikut setara:

    -keepclasseswithmembers class * { public static void main(java.lang.String[];) }
    
    -keepclasseswithmembers class ** { public static void main(java.lang.String[];) }
    

Aturan penyimpanan bersyarat

Selain aturan penyimpanan standar, Anda dapat menggunakan aturan penyimpanan bersyarat, yang hanya berlaku jika kondisi tertentu terpenuhi. Anda dapat menentukan aturan bersyarat menggunakan flag -if. Aturan penyimpanan yang mengikuti tanda -if hanya aktif jika spesifikasi class dalam tanda -if memiliki kecocokan.

Aturan tetap bersyarat sangat berguna saat menangani library atau pola kode yang menggunakan refleksi, yang hanya memerlukan aturan tetap jika class atau anggota tertentu ada atau cocok dengan pola. Menggunakan aturan kondisional membantu meminimalkan ukuran aplikasi Anda dengan mencegah retensi kode yang tidak perlu.

Sintaksis umum untuk aturan penyimpanan bersyarat adalah sebagai berikut:

-if <class_specification_if> <keep_rule>

Jika spesifikasi class dalam kondisi -if berisi karakter pengganti (seperti * atau **), urutan karakter yang cocok dengan karakter pengganti akan diambil. Anda dapat merujuk ke string yang diambil ini dalam aturan penyimpanan berikutnya menggunakan rujukan balik: <1> merujuk ke string yang diambil oleh karakter pengganti pertama, <2> merujuk ke string yang diambil oleh karakter pengganti kedua, dan seterusnya.

Misalnya, komponen Jetpack Navigation menghasilkan class NavArgs untuk argumen yang melewati antar-tujuan dengan keamanan jenis. Saat menggunakan delegasi NavArgsLazy, delegasi ini menggunakan refleksi untuk menemukan dan memanggil metode fromBundle statis pada class NavArgs yang dihasilkan untuk mendeserialisasi argumen. Jika aplikasi Anda menggunakan NavArgs, Anda hanya perlu menyimpan metode fromBundle untuk class yang mengimplementasikan antarmuka NavArgs.

Anda dapat menggunakan aturan keep bersyarat untuk menentukan bahwa jika ada class yang menerapkan androidx.navigation.NavArgs, maka R8 harus mempertahankan metode fromBundle untuk class tertentu tersebut:

# If a class implements NavArgs...
-if public class ** implements androidx.navigation.NavArgs
# ...then keep the fromBundle method of that matched class (<1>).
-keepclassmembers public class <1> {
    public static ** fromBundle(android.os.Bundle);
}

Dalam contoh ini, ** adalah karakter pengganti pertama dan satu-satunya dalam kondisi -if. Ini cocok dengan nama class dari class yang menerapkan androidx.navigation.NavArgs. String yang cocok dengan ** (dalam hal ini, nama class) akan diambil, dan Anda dapat merujuknya menggunakan <1> dalam aturan berikutnya. Dengan demikian, aturan -keepclassmembers berlaku untuk class apa pun yang mengimplementasikan androidx.navigation.NavArgs yang cocok dengan kondisi -if. Jika R8 tidak menemukan class yang menerapkan NavArgs, R8 akan mengabaikan aturan keep ini.

Kasus penggunaan umum lainnya adalah dengan library serialisasi JSON seperti Gson. Jika class model data Anda menggunakan anotasi @SerializedName Gson di kolom mana pun, Anda dapat menggunakan aturan bersyarat untuk melindungi class tersebut dan anggotanya yang diperlukan Gson untuk refleksi:

# If a class has fields annotated with @SerializedName...
-if class ** { @com.google.gson.annotations.SerializedName <fields>; }
# ...then keep that class (<1>), its @SerializedName fields,
# and its constructors for Gson.
-keep class <1> {
    @com.google.gson.annotations.SerializedName <fields>;
    <init>(...);
}

Referensi balik mengambil string, yang dapat berupa substring dari nama class jika karakter pengganti hanya cocok dengan sebagian nama. Misalnya, jika Anda menggunakan -if class com.example.*X*, R8 akan mengambil substring sebelum X sebagai <1> dan substring setelah X sebagai <2>. Aturan berikut menggunakan ini untuk menemukan nama class yang berisi X dan menyimpan class yang sesuai dengan X diganti dengan Y:

# If a class like com.example.PrefixXPostfix exists...
-if class com.example.*X*
# ...keep com.example.PrefixYPostfix.
-keep class com.example.<1>Y<2>

Aturan penyimpanan bersyarat untuk refleksi

Salah satu kasus penggunaan umum untuk aturan keep bersyarat adalah menangani refleksi, dengan metode atau class tertentu diakses secara dinamis saat runtime. Misalnya, jika library menggunakan refleksi untuk berinteraksi dengan kode Anda, Anda mungkin hanya perlu mempertahankan anggota tertentu jika Anda menggunakan fitur spesifik dari library tersebut.

Library Jetpack Navigation menggunakan refleksi menggunakan delegasi NavArgsLazy untuk memanggil metode fromBundle statis pada class NavArgs yang dihasilkan untuk penerusan argumen yang aman untuk jenis. Untuk memastikan metode ini hanya dipertahankan untuk implementasi NavArgs, dan bukan untuk setiap class, Jetpack Navigation menyertakan aturan keep bersyarat berikut:

# If a class implements NavArgs...
-if public class ** implements androidx.navigation.NavArgs
# ...then keep the fromBundle method of that matched class (<1>).
-keepclassmembers public class <1> {
    public static ** fromBundle(android.os.Bundle);
}

Aturan ini hanya menyimpan fromBundle untuk class yang memerlukannya, bukan menyimpannya di semua class atau mengharuskan Anda menentukan secara manual class mana yang memerlukannya.

Memeriksa nama Java yang dihasilkan

Saat menulis aturan penyimpanan, Anda harus menentukan class dan jenis referensi lainnya menggunakan namanya setelah dikompilasi ke bytecode Java (lihat Spesifikasi class dan Jenis untuk melihat contohnya). Untuk memeriksa nama Java yang dihasilkan untuk kode Anda, gunakan salah satu alat berikut di Android Studio:

  • APK Analyzer
  • Dengan file sumber Kotlin terbuka, periksa bytecode dengan membuka Tools > Kotlin > Show Kotlin Bytecode > Decompile.