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 aşağıdaki gibidir ancak bazı saklama seçenekleri isteğe bağlı keep_option_modfier öğesini kabul etmez.


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

Aşağıda, keepclassmembers öğesini saklama seçeneği, allowoptimization öğesini 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. Altı farklı saklama seçeneği vardır: keep, keepclassmembers, keepclasseswithmembers, keepnames, keepclassmembernames, keepclasseswithmembernames.

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

Keep seçeneği Açıklama
keepclassmembers Yalnızca optimizasyondan sonra sınıf varsa belirtilen üyeleri korur.
keep Belirtilen sınıfları ve belirtilen üyeleri (alanlar ve yöntemler) korur ve bunların optimize edilmesini engeller.

Not: keep, genellikle yalnızca koruma seçeneği değiştiricileriyle kullanılmalıdır. Bunun nedeni, keep tek başına kullanıldığında eşleşen sınıflarda her türlü optimizasyonun gerçekleşmesini engellemesidir.
keepclasseswithmembers Yalnızca sınıf spesifikasyonundaki tüm üyeler sınıfta varsa 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 nihai 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 Keep seçeneğini belirleme

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ı daraltı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.
allowobfucastion 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 tanımlayıcılarında (parametre türleri ve dönüş türleri) ve alanlarda (alan türleri) 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

Bir sınıf, üst sınıf veya uygulanan arayüzü keep kuralının bir parçası olarak belirtmeniz gerekir. java.lang ad alanındaki sınıflar (ör. java.lang.String) 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ı alma bölümünde açıklanan araçları kullanarak bayt kodunu inceleyin.

Aşağıdaki örnekte MaterialButton sınıfını nasıl belirtmeniz gerektiği gösterilmektedir:

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

Sınıf özellikleri, bir sınıfta hangi üyelerin tutulacağını da belirtir. Aşağıdaki kural, MaterialButton sınıfını ve tüm üyelerini korur:

-keep class com.google.android.material.button.MaterialButton { *; }

Alt sınıflar ve uygulamalar

Bir arayüzü uygulayan bir alt sınıfı veya 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 sınıfınız Bar 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 Bar üst sınıfını içermediğini unutmayın.

-keep class * extends Bar

Bar öğesini 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ştiricisi

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, Kotlin'e özgü değiştiricileri (ör. internal ve suspend) 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 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(...);
    }
    

Üyelik şartları

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 yalnızca bu üyeler için geçerli olur.

Ö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ı korumak ve üyelerini korumamak için aşağıdakileri kullanın:

-keep class com.myapp.MyClass

Çoğu zaman bazı üyeleri belirtmek istersiniz. Ö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);
}

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

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

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 koruma kuralı, void döndüren ve setLabel() alan setLabel() adlı herkese açık bir yöntemi korur.String

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

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.MyView {
    <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.

Üreticiler

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

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

Örneğin, aşağıdaki koruma kuralı, Context ve AttributeSet alan özel bir View oluşturucuyu korur.

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

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

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

Fields'ın oynadığı filmler

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 saklama 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>;
}

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 için 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);
}

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 keep kuralları yazdığınızda 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 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 yerine Object kullanılır. 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 yerine sınırlı türü 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) {
}

Hem includedescriptorclasses sınıfını hem de UnpackOptions sınıfı yöntemini tek bir kuralda korumak için aşağıdaki gibi bir koruma kuralı kullanabilirsiniz:Box

-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. Genel türler silindiğinden List<Product> gibi bir parametrenin java.util.List olarak görüleceğini unutmayın.

Örneğin, bir Product nesne listesini aşağıdaki gibi 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();
}

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 sık kullanılan. 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 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 Herhangi bir temel türle (ö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ı adlı 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ı tek bir karakterle değişen 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 saklama 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[];) }
    

Oluşturulan Java adlarını inceleme

Saklama kuralları yazarken, Java bayt koduna derlendikten sonra sınıfları ve diğer referans türlerini adlarıyla 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.