Saklama kuralları ekleme

Genel olarak, saklama kuralı bir sınıfı (veya alt sınıfı ya da uygulamayı) ve ardından bu sınıfın korunacak üyelerini (yöntemler, oluşturucular veya alanlar) belirtir.

Saklama kuralının genel söz dizimi şöyledir:


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

Aşağıda, keepclassmembers seçeneğini saklama seçeneği, allowoptimization seçeneğini değiştirici olarak kullanan ve someSpecificMethod() öğesini com.example.MyClass öğesinden saklayan bir saklama kuralı örneği verilmiştir:

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

Saklama seçeneği

Saklama seçeneği, saklama kuralınızın ilk bölümüdür. Sınıfın hangi yönlerinin korunacağını belirtir. keep, keepclassmembers, keepclasseswithmembers, keepnames, keepclassmembernames ve keepclasseswithmembernames olmak üzere altı farklı saklama seçeneği vardır.

Aşağıdaki tabloda bu saklama seçenekleri açıklanmaktadır:

Keep seçeneği Açıklama
keepclassmembers Yalnızca R8, kendilerini içeren sınıfı kaldırmazsa belirtilen üyeleri korur.
keep Belirtilen sınıfları ve belirtilen üyeleri (alanlar ve yöntemler) koruyarak bunların optimize edilmesini önler.

Not: keep, keep tek başına kullanıldığında eşleşen sınıflarda her türlü optimizasyonun gerçekleşmesini engellediği için genellikle yalnızca keep seçeneği değiştiricileriyle birlikte kullanılmalıdır.
keepclasseswithmembers Yalnızca sınıf spesifikasyonundaki tüm üyelere sahip olan sınıfları ve belirtilen üyelerini korur.
keepclassmembernames Belirtilen sınıf üyelerinin yeniden adlandırılmasını önler ancak sınıfın veya üyelerinin kaldırılmasını engellemez.

Not: Bu seçeneğin anlamı genellikle yanlış anlaşılır. Bunun yerine eşdeğer olan -keepclassmembers,allowshrinking seçeneğini kullanabilirsiniz.
keepnames Sınıfların ve üyelerinin yeniden adlandırılmasını engeller ancak kullanılmadığı düşünülen sınıfların tamamen kaldırılmasını engellemez.

Not: Bu seçeneğin anlamı genellikle yanlış anlaşılır. Bunun yerine eşdeğer olan -keep,allowshrinking seçeneğini kullanabilirsiniz.
keepclasseswithmembernames Sınıfların ve belirtilen üyelerinin yeniden adlandırılmasını engeller ancak yalnızca üyeler son kodda varsa. Kodun kaldırılmasını engellemez.

Not: Bu seçeneğin anlamı genellikle yanlış anlaşılır. Bunun yerine eşdeğer olan -keepclasseswithmembers,allowshrinking seçeneğini kullanabilirsiniz.

Doğru saklama seçeneğini belirleyin

Doğru tutma seçeneğini belirlemek, uygulamanız için doğru optimizasyonu belirlemek açısından çok önemlidir. Belirli tutma seçenekleri, referans verilmeyen kodun kaldırıldığı bir işlem olan kodu küçültürken diğerleri kodu karartır veya yeniden adlandırır. Aşağıdaki tabloda, çeşitli saklama seçeneklerinin işlemleri belirtilmektedir:

Keep seçeneği Sınıfları küçültür Sınıfları karartır Shrinks üyeleri Üyeleri karartır
keep
keepclassmembers
keepclasseswithmembers
keepnames
keepclassmembernames
keepclasseswithmembernames

Seçenek değiştiriciyi koruma

Bir saklama seçeneği değiştiricisi, saklama kuralının kapsamını ve davranışını kontrol etmek için kullanılır. Saklama kuralınıza 0 veya daha fazla saklama seçeneği değiştiricisi ekleyebilirsiniz.

Bir saklama seçeneği değiştiricisi için olası değerler aşağıdaki tabloda açıklanmıştır:

Değer Açıklama
allowoptimization Belirtilen öğelerin optimize edilmesine olanak tanır. Ancak belirtilen öğeler yeniden adlandırılmaz veya kaldırılmaz.
allowobfuscation Belirtilen öğelerin yeniden adlandırılmasına izin verir. Ancak öğeler kaldırılmaz veya başka bir şekilde optimize edilmez.
allowshrinking R8, belirtilen öğelere referans bulamazsa bunların kaldırılmasına izin verir. Ancak öğeler yeniden adlandırılmaz veya başka bir şekilde optimize edilmez.
includedescriptorclasses R8'e, tutulan yöntemlerin (parametre türleri ve dönüş türleri) ve alanların (alan türleri) tanımlayıcılarında görünen tüm sınıfları tutmasını söyler.
allowaccessmodification R8'in optimizasyon işlemi sırasında sınıfların, yöntemlerin ve alanların erişim değiştiricilerini (public, private, protected) değiştirmesine (genellikle genişletmesine) olanak tanır.
allowrepackage R8'in sınıfları varsayılan (kök) paket de dahil olmak üzere farklı paketlere taşımasına olanak tanır.

Sınıf özellikleri

Her saklama kuralında bir sınıf (arayüz, enum ve açıklama sınıfları dahil) belirtmeniz gerekir. İsteğe bağlı olarak, bir üst sınıf veya uygulanan arayüz belirterek ya da sınıf için erişim değiştiricisini belirterek kuralı notlara göre kısıtlayabilirsiniz. java.lang ad alanındaki java.lang.String gibi sınıflar da dahil olmak üzere tüm sınıflar, tam nitelikli Java adları kullanılarak belirtilmelidir. Kullanılması gereken adları anlamak için Oluşturulan Java adlarını inceleme bölümünde açıklanan araçları kullanarak bayt kodunu inceleyin.

Aşağıdaki örnekte MaterialButton sınıfını nasıl belirteceğiniz gösterilmektedir:

  • Doğru: com.google.android.material.button.MaterialButton
  • Yanlış: MaterialButton

Sınıf özellikleri, bir sınıfta tutulması gereken üyeleri de belirtir. Örneğin, aşağıdaki kural MyClass sınıfını ve someSpecificMethod() yöntemini korur:

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

Notlara göre sınıfları belirtme

Sınıfları ek açıklamalarına göre belirtmek için ek açıklamanın tam nitelikli Java adının başına @ sembolünü ekleyin. Örneğin:

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

Bir saklama kuralında birden fazla açıklama varsa listelenen açıklamaların tümüne sahip sınıflar saklanır. Birden fazla ek açıklama listeleyebilirsiniz ancak kural yalnızca sınıfta listelenen tüm ek açıklamalar varsa geçerlidir. Örneğin, aşağıdaki kural hem Annotation1 hem de Annotation2 ile açıklama eklenen tüm sınıfları korur.

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

Alt sınıfları ve uygulamaları belirtme

Bir alt sınıfı veya arayüz uygulayan bir sınıfı hedeflemek için sırasıyla extend ve implements kullanın.

Örneğin, aşağıdaki gibi Foo alt sınıfına sahip Bar sınıfınız varsa:

class Foo : Bar()

Aşağıdaki saklama kuralı, Bar öğesinin tüm alt sınıflarını korur. Saklama kuralının, üst sınıf Bar'ı içermediğini unutmayın.

-keep class * extends Bar

Bar arayüzünü uygulayan Foo sınıfınız varsa:

class Foo : Bar

Aşağıdaki saklama kuralı, Bar uygulayan tüm sınıfları korur. Saklama kuralının Bar arayüzünü içermediğini unutmayın.

-keep class * implements Bar

Erişim değiştiricilere göre sınıfları belirtin

Saklama kurallarınızı daha hassas hale getirmek için public, private, static ve final gibi erişim değiştiricileri belirtebilirsiniz.

Örneğin, aşağıdaki kural, api paketindeki ve alt paketlerindeki tüm public sınıflarını ve bu sınıflardaki tüm herkese açık ve korumalı üyeleri tutar.

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

Bir sınıftaki üyeler için de değiştiriciler kullanabilirsiniz. Örneğin, aşağıdaki kural yalnızca Utils sınıfının public static yöntemlerini tutar:

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

Kotlin'e özgü değiştiriciler

R8, internal ve suspend gibi Kotlin'e özgü değiştiricileri desteklemez. Bu tür alanları korumak için aşağıdaki yönergeleri kullanın.

  • Bir internal sınıfını, yöntemini veya alanını korumak için bunu herkese açık olarak değerlendirin. Örneğin, aşağıdaki Kotlin kaynağını ele alalım:

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

    internal sınıfları, yöntemleri ve alanları Kotlin derleyicisi tarafından oluşturulan .class dosyalarda public olduğundan aşağıdaki örnekte gösterildiği gibi public anahtar kelimesini kullanmanız gerekir:

    -keepclassmembers public class com.example.ImportantInternalClass {
      public int f;
      public void m();
    }
    
  • Bir suspend üyesi derlendiğinde, derlenmiş bayt kodu imzasını keep kuralında eşleştirin.

    Örneğin, Kotlin'de aşağıdaki gibi tanımlanan somut bir gerçek dünya deposu sınıfını ele alalım:

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

    Kotlin derleyicisi bu sınıfı bayt koduna derlediğinde suspend işlevler, devamlılık aktarma stili (CPS) dönüşümüne tabi tutulur. suspend değiştiricisi kaldırılır, dönüş türü java.lang.Object olarak değiştirilir ve eşzamansız durum makinesini yönetmek için yöntem imzasına kotlin.coroutines.Continuation parametresi eklenir.

    Bayt kodundaki derlenmiş yöntem imzası aşağıdaki gibi görünür:

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

    Bu işlev için saklama kuralı yazmak üzere Kotlin suspend söz dizimini kullanamazsınız. Bunun yerine, derlenmiş bayt kodu imzasını (özellikle kotlin.coroutines.Continuation'yı referans alarak) eşleştirmeniz veya ...'ı kullanmanız gerekir.

    Derlenmiş tam bayt kodu imzasıyla eşleşen bir örnek aşağıda verilmiştir:

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

    ... kullanılan bir örneği aşağıda bulabilirsiniz:

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

Üye spesifikasyonu

Sınıf belirtimi, isteğe bağlı olarak korunacak sınıf üyelerini içerir. Bir sınıf için bir veya daha fazla üye belirtirseniz kural diğer üyeler için geçerli olmaz.

Üyeleri açıklamalara göre belirleme

Üyeleri ek açıklamalarına göre belirleyebilirsiniz. Sınıflara benzer şekilde, ek açıklamanın tam nitelikli Java adının önüne @ ekleyin. Bu sayede, bir sınıfta yalnızca belirli açıklamalarla işaretlenmiş üyeleri tutabilirsiniz. Örneğin, @com.example.MyAnnotation ile açıklama eklenmiş yöntemleri ve alanları korumak için:

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

Güçlü ve hedeflenmiş kurallar için bunu sınıf düzeyinde açıklama eşleştirme ile birleştirebilirsiniz:

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

Bu, @ClassAnnotation ile ek açıklama eklenen sınıfları ve bu sınıflarda @MethodAnnotation ile ek açıklama eklenen yöntemleri ve @FieldAnnotation ile ek açıklama eklenen alanları korur.

Mümkün olduğunda açıklama tabanlı saklama kurallarını kullanmayı düşünebilirsiniz. Bu yaklaşım, kodunuz ile saklama kurallarınız arasında açık bir bağlantı sağlar ve genellikle daha sağlam yapılandırmalara yol açar. Örneğin, androidx.annotation ek açıklama kitaplığı bu mekanizmayı kullanır.

Yöntemler

Saklama kuralı için üye spesifikasyonunda bir yöntem belirtmenin söz dizimi aşağıdaki gibidir:

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

Örneğin, aşağıdaki keep kuralı, String döndüren getUserId() adlı herkese açık bir yöntemi korur.

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

Bir sınıftaki tüm yöntemleri aşağıdaki gibi eşleştirmek için <methods> kısayolunu kullanabilirsiniz:

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

Dönüş türleri ve parametre türleri için türlerin nasıl belirtileceği hakkında daha fazla bilgi edinmek için Türler bölümüne bakın.

Markalar

Oluşturucu belirtmek için <init> kullanın. Bir saklama kuralının üye spesifikasyonunda oluşturucu belirtme söz dizimi aşağıdaki gibidir:

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

Örneğin, aşağıdaki koruma kuralı, bir repository örneği alan UI durum bilgisi depolayıcısı için bir oluşturucuyu korur.

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

Herkese açık tüm oluşturucuları tutmak için aşağıdaki örneği referans olarak kullanın:

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

Alanlar

Bir saklama kuralı için üye spesifikasyonunda alan belirtme söz dizimi aşağıdaki gibidir:

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

Örneğin, aşağıdaki keep kuralı userId adlı özel bir dize alanını ve STATUS_ACTIVE adlı herkese açık statik bir tam sayı alanını saklar:

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

Bir sınıftaki tüm alanları aşağıdaki gibi eşleştirmek için <fields> kısayolunu kullanabilirsiniz:

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

Türler

Bu bölümde, saklama kuralı üye spesifikasyonlarında dönüş türlerinin, parametre türlerinin ve alan türlerinin nasıl belirtileceği açıklanmaktadır. Kotlin kaynak kodundan farklı türler belirtmek için oluşturulan Java adlarını kullanmayı unutmayın.

Temel türler

Bir temel türü belirtmek için Java anahtar kelimesini kullanın. R8, aşağıdaki temel türleri tanır: boolean, byte, short, char, int, long, float, double.

Temel tür içeren bir örnek kural:

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

Genel: türler

Derleme sırasında Kotlin/Java derleyicisi genel tür bilgilerini siler. Bu nedenle, genel türleri içeren koruma kuralları yazarken kodunuzun derlenmiş gösterimini hedeflemeniz gerekir. Orijinal kaynak kodu hedeflememelisiniz. Jenerik türlerin nasıl değiştirildiği hakkında daha fazla bilgi edinmek için Tür silme başlıklı makaleyi inceleyin.

Örneğin, Box.kt içinde tanımlanmış sınırsız bir genel tür içeren aşağıdaki koda sahipseniz:

package com.myapp.data

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

Tür silme işleminden sonra T, Object ile değiştirilir. Sınıf oluşturucuyu ve yöntemi korumak için kuralınızda genel T yerine java.lang.Object kullanılmalıdır.

Örnek bir saklama kuralı aşağıdaki gibi olabilir:

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

NumberBox.kt içinde sınırlanmış bir genel tür içeren aşağıdaki koda sahipseniz:

package com.myapp.data

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

Bu durumda, tür silme işlemi T değerini bağlı olduğu java.lang.Number değeriyle değiştirir.

Örnek bir saklama kuralı aşağıdaki gibi olabilir:

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

Uygulamaya özel genel türler temel sınıf olarak kullanılırken temel sınıflar için de saklama kurallarının eklenmesi gerekir.

Örneğin, aşağıdaki kod için:

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) {
}

Hem UnpackOptions sınıfını hem de Box sınıf yöntemini tek bir kuralda korumak için includedescriptorclasses ile birlikte bir saklama kuralı kullanabilirsiniz.

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

Bir nesne listesini işleyen belirli bir işlevi korumak için işlevin imzasıyla tam olarak eşleşen bir kural yazmanız gerekir. Jenerik türler silindiğinden List<Product> gibi bir parametrenin java.util.List olarak görüldüğünü unutmayın.

Örneğin, bir Product nesne listesini aşağıdaki şekilde işleyen bir işlevi olan bir yardımcı sınıfınız varsa:

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)

Yalnızca processProducts işlevini korumak için aşağıdaki saklama kuralını kullanabilirsiniz:

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

Dizi türleri

Dizinin her boyutu için bileşen türüne [] ekleyerek bir dizi türü belirtin. Bu durum hem sınıf türleri hem de temel türler için geçerlidir.

  • Tek boyutlu sınıf dizisi: java.lang.String[]
  • İki boyutlu temel dizi: int[][]

Örneğin, aşağıdaki koda sahipseniz:

package com.example.data

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

Aşağıdaki saklama kuralını kullanabilirsiniz:

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

Örnekler

Örneğin, belirli bir sınıfı ve tüm üyelerini korumak için aşağıdakileri kullanın:

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

Yalnızca sınıfı ve varsayılan oluşturucusunu korumak, ancak diğer üyeleri korumamak için aşağıdakileri kullanın:

-keep class com.myapp.MyClass

Her zaman bazı üyeleri belirtmeniz önerilir. Örneğin, aşağıdaki örnekte herkese açık alan text ve herkese açık yöntem updateText(), MyClass sınıfında tutulur.

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

Herkese açık tüm alanları ve yöntemleri korumak için aşağıdaki örneğe bakın:

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

Üye belirtimini atlama

Üye belirtiminin atlanması, R8'in sınıf için varsayılan oluşturucuyu korumasına neden olur.

Örneğin, -keep class com.example.MyClass veya -keep class com.example.MyClass {} yazarsanız R8 bunları aşağıdaki gibi yazmışsınız gibi değerlendirir:

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

Üye adı kalıplarını olumsuzlama

Android Gradle eklentisi (AGP) 9.2.0'dan itibaren, koruma kurallarınızdaki üye adı kalıplarını olumsuzlayabilirsiniz. Bu sayede, kalıplara göre tutulacak veya hariç tutulacak üyeleri belirleyebilirsiniz. Bir kalıbı olumsuzlamak için üye adı kalıbının başına ünlem işareti (!) ekleyin.

Bu özelliği, daha geniş bir kalıba uyan üyelerin çoğunu tutmak ancak yalnızca test amaçlı yöntemler gibi belirli üyeleri hariç tutmak istediğiniz durumlarda kullanabilirsiniz.

Örnek:

com.example.MyClass içindeki "ForTesting" ile bitenler dışındaki tüm herkese açık yöntemleri korumak için aşağıdaki kuralı kullanın:

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

Paket düzeyindeki işlevler

Bir sınıfın dışında tanımlanan bir Kotlin işlevine (genellikle üst düzey işlevler olarak adlandırılır) referans vermek için Kotlin derleyicisi tarafından örtülü olarak eklenen sınıfın oluşturulan Java adını kullandığınızdan emin olun. Sınıf adı, Kt eklenmiş Kotlin dosya adıdır. Örneğin, aşağıdaki gibi tanımlanmış MyClass.kt adlı bir Kotlin dosyanız varsa:

package com.example.myapp.utils

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

isEmailValid işlevi için saklama kuralı yazmak üzere sınıf belirtiminin, oluşturulan MyClassKt sınıfını hedeflemesi gerekir:

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

Joker karakterler

Aşağıdaki tabloda, belirli bir kalıba uyan birden fazla sınıfa veya üyeye saklama kurallarını uygulamak için joker karakterlerin nasıl kullanılacağı gösterilmektedir.

Joker Sınıflar veya üyeler için geçerlidir Açıklama
** Her ikisi de En çok tercih edilen. Herhangi bir paket ayırıcı sayısı da dahil olmak üzere herhangi bir tür adıyla eşleşir. Bu, bir paketteki ve alt paketlerindeki tüm sınıfları eşleştirmek için kullanışlıdır.
* Her ikisi de Sınıf belirtimleri için paket ayırıcıları (.) içermeyen bir tür adının herhangi bir bölümüyle eşleşir.
Üye belirtimleri için herhangi bir yöntem veya alan adıyla eşleşir. Tek başına kullanıldığında ** için de takma ad görevi görür.
? Her ikisi de Bir sınıf veya üye adındaki herhangi bir tek karakterle eşleşir.
*** Üyeler İlkel türler (ör. int), sınıf türleri (ör. java.lang.String) ve herhangi bir boyuttaki dizi türleri (ör. byte[][]) dahil olmak üzere tüm türlerle eşleşir.
... Üyeler Bir yöntemin herhangi bir parametre listesiyle eşleşir.
% Üyeler Tüm temel türlerle (ör. int, float, boolean vb.) eşleşir.

Özel joker karakterlerin nasıl kullanılacağına dair bazı örnekler:

  • Giriş olarak farklı temel türler alan aynı ada sahip birden fazla yönteminiz varsa hepsini saklayan bir saklama kuralı yazmak için % kullanabilirsiniz. Örneğin, bu DataStore sınıfında birden fazla setValue yöntemi vardır:

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

    Aşağıdaki saklama kuralı tüm yöntemleri saklar:

    -keep class com.example.DataStore {
        public void setValue(java.lang.String, %);
    }
    
  • Adları tek bir karakterle değişen birden fazla sınıfınız varsa hepsini saklayacak bir saklama kuralı yazmak için ? simgesini kullanın. Örneğin, aşağıdaki sınıflarınız varsa:

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

    Aşağıdaki saklama kuralı tüm sınıfları saklar:

    -keep class com.example.models.UserV?
    
  • Example ve AnotherExample sınıflarını (kök düzeyinde sınıflarsa) com.foo.Example eşleştirmek için aşağıdaki koruma kuralını kullanın:

    -keep class *Example
    
  • Tek başına kullanılan * işareti, ** için takma ad görevi görür. Örneğin, aşağıdaki saklama kuralları eşdeğerdir:

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

Koşullu saklama kuralları

Standart saklama kurallarına ek olarak, yalnızca belirli bir koşul karşılandığında geçerli olan koşullu saklama kurallarını da kullanabilirsiniz. -if işaretini kullanarak koşullu kurallar belirleyebilirsiniz. Bir -if işaretini izleyen saklama kuralı yalnızca -if işaretindeki sınıf belirtimi eşleşiyorsa etkindir.

Koşullu saklama kuralları, özellikle yansıtma kullanan kitaplıklar veya kod kalıplarıyla çalışırken kullanışlıdır. Bu durumda, belirli bir sınıf veya üye mevcutsa ya da bir kalıpla eşleşiyorsa saklama kuralı gerekir. Koşullu kurallar kullanmak, gereksiz kod saklamayı önleyerek uygulamanızın boyutunu küçültmenize yardımcı olur.

Koşullu saklama kuralının genel söz dizimi şu şekildedir:

-if <class_specification_if> <keep_rule>

Bir -if koşulundaki sınıf spesifikasyonu joker karakterler (ör. * veya **) içeriyorsa joker karakterle eşleşen karakter dizisi yakalanır. Bu yakalanan dizelere, sonraki saklama kuralında geri referanslar kullanarak başvurabilirsiniz: <1>, ilk joker karakterle yakalanan dizeye, <2> ise ikinci joker karakterle yakalanan dizeye karşılık gelir.

Örneğin, Jetpack Navigation bileşeni, hedefler arasında tür güvenli bağımsız değişken geçişi için NavArgs sınıfları oluşturur. NavArgsLazy Yetkilendirme kullanılırken bağımsız değişkenlerin seri durumundan çıkarılması için yansıtma kullanılarak oluşturulan NavArgs sınıfında statik bir fromBundle yöntemi bulunur ve çağrılır. Uygulamanızda NavArgs kullanılıyorsa yalnızca NavArgs arayüzünü uygulayan sınıflar için fromBundle yöntemini tutmanız gerekir.

Herhangi bir sınıf androidx.navigation.NavArgs uyguluyorsa R8'in bu sınıf için fromBundle yöntemini saklaması gerektiğini belirtmek üzere koşullu bir saklama kuralı kullanabilirsiniz:

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

Bu örnekte, **, -if koşulundaki ilk ve tek joker karakterdir. androidx.navigation.NavArgs uygulayan herhangi bir sınıfın sınıf adıyla eşleşir. ** Eşleşen dize (bu örnekte sınıf adı) yakalanır ve sonraki kuralda <1> kullanarak buna başvurabilirsiniz. Bu nedenle -keepclassmembers kuralı, -if koşuluyla eşleşen androidx.navigation.NavArgs uygulayan tüm sınıflar için geçerlidir. R8, NavArgs uygulayan sınıf bulamazsa bu koruma kuralını yoksayar.

Bir diğer yaygın kullanım alanı da Gson gibi JSON serileştirme kitaplıklarıdır. Veri modeli sınıflarınız herhangi bir alanda Gson'un @SerializedName ek açıklamasını kullanıyorsa Gson'un yansıtma için ihtiyaç duyduğu bu tür sınıfları ve üyelerini korumak için koşullu bir kural kullanabilirsiniz:

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

Geriye referanslar, joker karakter yalnızca adın bir kısmıyla eşleşiyorsa sınıf adlarının alt dizeleri olabilen dizeleri yakalar. Örneğin, -if class com.example.*X* kullanıyorsanız R8, X öncesindeki alt dizeyi <1>, X sonrasındaki alt dizeyi ise <2> olarak yakalar. Aşağıdaki kural, X içeren sınıf adlarını bulmak ve X yerine Y'nin kullanıldığı ilgili sınıfı korumak için bu işlevi kullanır:

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

Yansıtma için koşullu saklama kuralları

Koşullu saklama kurallarının yaygın kullanım alanlarından biri, belirli yöntemlere veya sınıflara çalışma zamanında dinamik olarak erişildiği yansıtmayı işlemektir. Örneğin, bir kitaplık kodunuzla etkileşim kurmak için yansıtma kullanıyorsa bu kitaplığın belirli bir özelliğini kullanıyorsanız yalnızca belirli üyeleri saklamanız gerekebilir.

Jetpack Navigation kitaplığı, tür açısından güvenli bağımsız değişken geçirme için oluşturulan NavArgs sınıflarında statik bir fromBundle yöntemini çağırmak üzere NavArgsLazy delegate kullanarak yansıtma kullanır. Bu yöntemin yalnızca NavArgs uygulamaları için saklanmasını ve her sınıf için saklanmamasını sağlamak amacıyla Jetpack Navigation aşağıdaki koşullu saklama kuralını içerir:

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

Bu kural, fromBundle özelliğini tüm sınıflarda tutmak veya hangi sınıfların bu özelliğe ihtiyacı olduğunu manuel olarak belirtmenizi istemek yerine yalnızca ihtiyacı olan sınıflarda tutar.

Oluşturulan Java adlarını inceleme

Saklama kuralları yazarken, sınıfları ve diğer referans türlerini Java bayt koduna derlendikten sonra adlarını kullanarak belirtmeniz gerekir (örnekler için Sınıf belirtimi ve Türler'e bakın). Kodunuz için oluşturulan Java adlarının ne olduğunu kontrol etmek üzere Android Studio'da aşağıdaki araçlardan birini kullanın:

  • APK Analiz Aracı
  • Kotlin kaynak dosyası açıkken Tools > Kotlin > Show Kotlin Bytecode > Decompile'a (Araçlar > Kotlin > Kotlin Bytecode'u Göster > Derlemeyi Kaldır) giderek bytecode'u inceleyin.