Menambahkan aturan simpan

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

Sintaksis umum untuk aturan keep adalah sebagai berikut:


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

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

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

Opsi simpan

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

Tabel berikut menjelaskan opsi penyimpanan ini:

Opsi simpan Deskripsi
keepclassmembers Mempertahankan anggota yang ditentukan hanya jika R8 tidak menghapus class yang berisi anggota tersebut.
keep Mempertahankan class dan anggota (kolom dan metode) yang ditentukan, sehingga tidak dioptimalkan.

Catatan: keep umumnya hanya boleh digunakan dengan pengubah opsi keep karena keep sendiri mencegah terjadinya pengoptimalan apa pun pada class yang cocok.
keepclasseswithmembers Mempertahankan class dan anggota yang ditentukan hanya jika class tersebut 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 class dan anggotanya, tetapi tidak mencegahnya dihapus sepenuhnya jika dianggap tidak digunakan.

Catatan: Arti opsi ini sering disalahpahami; sebaiknya gunakan -keep,allowshrinking yang setara.
keepclasseswithmembernames Mencegah penggantian nama class dan anggota yang ditentukan, tetapi hanya jika anggota tersebut 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 keep:

Opsi simpan Menyusutkan class Meng-obfuscate class Menyusutkan anggota Meng-obfuscate anggota
keep
keepclassmembers
keepclasseswithmembers
keepnames
keepclassmembernames
keepclasseswithmembernames

Pengubah opsi simpan

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

Kemungkinan nilai untuk pengubah opsi keep dijelaskan dalam tabel berikut:

Nilai Deskripsi
allowoptimization Memungkinkan pengoptimalan elemen yang ditentukan. Namun, elemen yang ditentukan tidak diganti namanya atau dihapus.
allowobfuscation Mengizinkan penggantian nama elemen yang ditentukan. Namun, elemen tidak dihapus atau dioptimalkan.
allowshrinking Mengizinkan 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 Memungkinkan 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 class 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 diberi anotasi 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 subclass 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 keep 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 mempertahankan semua class public dalam paket api dan sub-paketnya, serta semua anggota publik dan yang dilindungi di class ini.

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

Anda juga dapat menggunakan pengubah untuk anggota dalam class. Misalnya, aturan berikut hanya mempertahankan 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, sehingga 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 bytecode yang dikompilasi dalam aturan keep.

    Misalnya, pertimbangkan class repositori konkret di dunia nyata yang ditentukan di Kotlin sebagai berikut:

    package com.example.repository
    
    import com.example.model.User
    
    class UserRepository {
        suspend fun fetchUser(id: String): User {
            // Implementation details...
        }
    }
    

    Saat compiler Kotlin mengompilasi class ini menjadi bytecode, fungsi suspend mengalami transformasi Continuation-Passing Style (CPS). Pengubah suspend dihapus, jenis nilai yang ditampilkan diubah menjadi java.lang.Object, dan parameter kotlin.coroutines.Continuation ditambahkan ke tanda tangan metode untuk mengelola mesin status asinkron.

    Tanda tangan metode yang dikompilasi dalam bytecode akan muncul sebagai berikut:

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

    Untuk menulis aturan keep untuk fungsi ini, Anda tidak dapat menggunakan sintaksis suspend Kotlin. Sebagai gantinya, Anda harus mencocokkan tanda tangan bytecode yang dikompilasi (khususnya mereferensikan kotlin.coroutines.Continuation), atau menggunakan ....

    Contoh yang cocok dengan signature bytecode yang dikompilasi secara tepat 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 sebuah class, aturan tidak akan 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>;
}

Tindakan ini akan mempertahankan class yang dianotasi dengan @ClassAnnotation, dan pada class tersebut, akan mempertahankan metode yang dianotasi oleh @MethodAnnotation dan kolom yang dianotasi oleh @FieldAnnotation.

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

Untuk refleksi dalam kode Anda sendiri, Anda juga dapat menggunakan Jetpack Keep. Daripada menulis aturan penyimpanan yang menargetkan anotasi, Anda dapat menganotasi kode, dan plugin Gradle akan membuat aturan penyimpanan untuk Anda.

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 getUserId() yang menampilkan String.

-keep class com.example.model.UserData {
    public java.lang.String getUserId();
}

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

-keep class com.example.model.UserData {
    <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 keep adalah sebagai berikut:

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

Misalnya, aturan penyimpanan berikut menyimpan konstruktor untuk holder status UI yang mengambil instance repositori.

-keep class com.example.ui.state.UserViewModel {
    public <init>(com.example.repository.UserDataRepository);
}

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

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

Kolom

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

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

Misalnya, aturan keep berikut mempertahankan kolom string pribadi yang disebut userId dan kolom bilangan bulat statis publik yang disebut 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 di 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 keep. Ingatlah 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, sehingga saat menulis aturan keep yang melibatkan jenis generik, Anda harus menargetkan representasi kode yang dikompilasi, bukan kode sumber asli. Untuk mempelajari lebih lanjut cara mengubah jenis generik, lihat Penghapusan jenis.

Misalnya, jika Anda memiliki kode berikut dengan jenis generik tidak terbatas yang ditentukan di 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 keep 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 terbatas 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 perlu menyertakan aturan keep untuk class dasar juga.

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 keep 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 perlu menulis aturan yang cocok dengan tanda tangan fungsi tersebut. Perhatikan bahwa karena jenis generik dihapus, parameter seperti List<Product> akan terlihat 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 penyimpanan berikut untuk melindungi fungsi processProducts saja:

-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 hal berikut:

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

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

-keep class com.myapp.MyClass

Sebaiknya Anda selalu menentukan beberapa anggota. Misalnya, contoh berikut mempertahankan 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 akan 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 hal berikut:

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

Meniadakan pola nama anggota

Mulai dari Plugin Android Gradle (AGP) 9.2.0, Anda dapat meniadakan pola nama anggota dalam aturan keep. Dengan begitu, Anda dapat menentukan anggota yang akan dipertahankan atau dikecualikan berdasarkan pola. Untuk meniadakan pola, beri awalan pola nama anggota dengan tanda seru (!).

Anda dapat menggunakan fitur ini untuk situasi saat Anda ingin mempertahankan sebagian besar anggota yang cocok dengan pola yang lebih luas, tetapi mengecualikan anggota tertentu, seperti metode yang hanya ditujukan untuk pengujian.

Contoh:

Untuk mempertahankan semua metode publik di com.example.MyClass kecuali yang diakhiri dengan "ForTesting", gunakan aturan berikut:

-keepclassmembers class com.example.MyClass {
    public *** !*ForTesting(...);
}

Fungsi tingkat paket

Untuk mereferensikan fungsi Kotlin yang ditentukan di luar class (biasanya disebut fungsi tingkat teratas), pastikan untuk menggunakan nama Java yang dihasilkan untuk class yang ditambahkan secara implisit oleh compiler Kotlin. Nama class adalah nama file Kotlin dengan tambahan Kt. 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 yang dihasilkan MyClassKt:

-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 keep ke beberapa class atau anggota yang cocok dengan pola tertentu.

Karakter pengganti Berlaku untuk kelas atau anggota Deskripsi
** Keduanya Paling umum 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, fungsi ini juga merupakan alias untuk **.
? Keduanya Mencocokkan karakter tunggal apa pun dalam nama class atau anggota.
*** Anggota Cocok dengan jenis apa pun, termasuk jenis primitif (seperti int), jenis class (seperti java.lang.String), dan jenis array dari dimensi apa pun (seperti byte[][]).
... Anggota Mencocokkan daftar parameter untuk suatu metode.
% Anggota Cocok dengan 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 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 keep berikut mempertahankan 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 * saja, karakter ini akan bertindak sebagai alias untuk **. Misalnya, aturan keep berikut ini 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 keep bersyarat sangat berguna saat menangani library atau pola kode yang menggunakan refleksi, yang mana aturan keep hanya diperlukan jika class atau anggota tertentu ada atau cocok dengan pola. Penggunaan aturan bersyarat membantu meminimalkan ukuran aplikasi Anda dengan mencegah retensi kode yang tidak perlu.

Sintaksis umum untuk aturan keep 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 ditangkap. 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 Navigasi Jetpack menghasilkan class NavArgs untuk penerusan argumen dengan keamanan jenis antar-tujuan. Saat menggunakan delegasi NavArgsLazy, delegasi ini menggunakan refleksi untuk menemukan dan memanggil metode fromBundle statis pada class NavArgs yang dihasilkan untuk melakukan deserialisasi argumen. Jika aplikasi Anda menggunakan NavArgs, Anda hanya perlu mempertahankan 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 apa pun yang mengimplementasikan androidx.navigation.NavArgs. String yang cocok dengan ** (dalam hal ini, nama class) akan diambil, dan Anda dapat merujuknya menggunakan <1> dalam aturan berikutnya. Oleh karena itu, 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 penyimpanan 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 menangkap string, yang dapat berupa substring 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 penyimpanan bersyarat adalah menangani refleksi, dengan metode atau class tertentu diakses secara dinamis saat runtime. Misalnya, jika library menggunakan refleksi untuk berinteraksi dengan kode, Anda mungkin hanya perlu mempertahankan anggota tertentu jika menggunakan fitur tertentu dari library tersebut.

Library Jetpack Navigation menggunakan refleksi menggunakan delegasi NavArgsLazy untuk memanggil metode fromBundle statis pada class NavArgs yang dihasilkan untuk penerusan argumen keamanan jenis. Untuk memastikan metode ini hanya dipertahankan untuk penerapan NavArgs, dan bukan untuk setiap class, Jetpack Navigation menyertakan aturan tetap 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 mempertahankan fromBundle untuk class yang memerlukannya, bukan mempertahankannya di semua class atau mengharuskan Anda menentukan secara manual class mana yang memerlukannya.

Memeriksa nama Java yang dihasilkan

Saat menulis aturan keep, Anda harus menentukan class dan jenis referensi lainnya menggunakan namanya setelah dikompilasi ke dalam bytecode Java (lihat Spesifikasi class dan Jenis untuk contoh). 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.