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 şu şekildedir:


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

Aşağıda, keepclassmembers saklama seçeneğini, allowoptimization değiştiricisini 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 Belirtilen üyeleri yalnızca R8, bunları içeren sınıfı kaldırmazsa korur.
keep Belirtilen sınıfları ve belirtilen üyeleri (alanlar ve yöntemler) koruyarak bunların optimize edilmesini önler.

Not: 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.keep
keepclasseswithmembers Yalnızca sınıf spesifikasyonundaki tüm üyelere sahipse bir sınıfı ve belirtilen üyelerini korur.
keepclassmembernames Belirtilen sınıf üyelerinin yeniden adlandırılmasını engeller 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 belirleme

Uygulamanız için doğru optimizasyonu belirlemede doğru koruma seçeneğini belirlemek çok önemlidir. Belirli koruma 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 gösterilmektedir:

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

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 koruma 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 bu öğelerin 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. İsterseniz bir üst sınıf veya uygulanan arayüz belirterek ya da sınıf için erişim değiştiricisini belirterek kuralı ek açıklamalar temelinde 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 hangi üyelerin korunması gerektiğini 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 önüne @ simgesini ekleyin. Örneğin:

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

Bir saklama kuralında birden fazla açıklama varsa listedeki tüm açıklamalara 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çerli olur. Ö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 alt sınıfı Foo olan Bar sınıfınız varsa:

class Foo : Bar()

Aşağıdaki saklama kuralı, Bar sınıfının 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

Not alma 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 genel 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 bir 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 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() {}
    }
    

    Kotlin derleyicisi tarafından oluşturulan .class dosyalarında internal sınıfları, yöntemleri ve alanları 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, derlenen imzasını saklama kuralında eşleştirin.

    Örneğin, fetchUser işlevini aşağıdaki snippet'te gösterildiği gibi tanımladıysanız:

    suspend fun fetchUser(id: String): User
    

    Derlendiğinde, bayt kodundaki 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 bu derlenmiş imzayla eşleşmeniz veya ... kullanmanız gerekir.

    Derlenmiş imzayı kullanma örneği aşağıda verilmiştir:

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

    ... kullanan 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 belirtebilirsiniz. Sınıflara benzer şekilde, açıklamanın tam nitelikli Java adının önüne @ ekleyin. Bu sayede, bir sınıfta yalnızca belirli ek 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 ek 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 belirtme söz dizimi aşağıdaki gibidir:

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

Örneğin, aşağıdaki keep kuralı getUserId() adlı ve String döndüren 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 başlıklı makaleyi inceleyin.

Markalar

Bir oluşturucu belirtmek için <init> kullanın. Saklama kuralı için ü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 depo örneği alan UI durum bilgisi depolayıcı 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

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ı korur:

-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 kural örneği:

# 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 orijinal kaynak kodu değil, kodunuzun derlenmiş gösterimini hedeflemeniz gerekir. Jenerik türlerin nasıl değiştirildiği hakkında daha fazla bilgi edinmek için Tür silme konusuna bakın.

Ö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 kodunuz varsa:

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 sınırlı değeri olan java.lang.Number ile 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ürleri temel sınıf olarak kullanırken temel sınıflar için de saklama kurallarını eklemek 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) {
}

includedescriptorclasses ile birlikte bir saklama kuralı kullanarak hem UnpackOptions sınıfını hem de Box sınıfı yöntemini tek bir kuralda aşağıdaki gibi koruyabilirsiniz:

-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 text herkese açık alanı ve updateText() herkese açık yöntemi MyClass sınıfında tutulur.

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

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

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

Üye belirtimini atlama

Üye spesifikasyonunun 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-alpha02'den itibaren, koruma kurallarınızda ü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 önüne ünlem işareti (!) ekleyin.

Bu özelliği, daha geniş bir kalıba uyan üyelerin çoğunu tutmak ancak yalnızca test için tasarlanmış 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 karakter Sınıflar veya üyeler için geçerlidir Açıklama
** Her ikisi de En çok tercih edilen. 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 spesifikasyonları 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 spesifikasyonları için herhangi bir yöntem veya alan adıyla eşleşir. Tek başına kullanıldığında ** için de takma ad olarak kullanılı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 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ı örnekleri aşağıda bulabilirsiniz:

  • Giriş olarak farklı temel türleri alan aynı ada sahip birden fazla yönteminiz varsa hepsini tutan bir tutma 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ı bir karakter farklı olan birden fazla sınıfınız varsa hepsini saklayacak bir saklama kuralı yazmak için ? 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ıflar olmaları durumunda) eşleştirmek ancak com.foo.Example sınıfını eşleştirmemek için aşağıdaki tutma kuralını kullanın:

    -keep class *Example
    
  • * tek başına kullanıldığında ** 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 belirtebilirsiniz. -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, yalnızca belirli bir sınıf veya üye varsa ya da bir kalıpla eşleşiyorsa saklama kuralı gerekir. Koşullu kurallar kullanmak, gereksiz kodların tutulmasını önleyerek uygulamanızın boyutunu en aza indirmenize yardımcı olur.

Koşullu saklama kuralının genel söz dizimi aşağıdaki gibidir:

-if <class_specification_if> <keep_rule>

Bir -if koşulundaki sınıf belirtimi 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 iletme için NavArgs sınıfları oluşturur. NavArgsLazy delegate kullanılırken bağımsız değişkenlerin seri durumdan çıkarılması için oluşturulan fromBundle sınıfında statik bir NavArgs yöntemi bulup çağırmak üzere yansıtma kullanı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 fromBundle yöntemini o sınıf için 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. ** ile eşleşen dize (bu örnekte sınıf adı) yakalanır ve sonraki kuralda <1> kullanarak buna referans verebilirsiniz. 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 herhangi bir sınıf adını bulmak ve X yerine Y'nin kullanıldığı ilgili sınıfı korumak için bu özelliği 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>

Düşünme 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 NavArgsLazy sınıflarında statik bir fromBundle yöntemi çağırmak üzere NavArgs 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'ı tüm sınıflarda tutmak veya hangi sınıfların fromBundle'a 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, Java bayt koduna derlendikten sonra sınıfları ve diğer referans türlerini adlarını kullanarak belirtmeniz gerekir (örnekler için Sınıf spesifikasyonu 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 Analyzer
  • Kotlin kaynak dosyası açıkken Tools > Kotlin > Show Kotlin Bytecode > Decompile'a (Araçlar > Kotlin > Kotlin Bayt Kodunu Göster > Derlemeyi Kaldır) giderek bayt kodunu inceleyin.