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ındainternal
sınıfları, yöntemleri ve alanlarıpublic
olduğundan aşağıdaki örnekte gösterildiği gibipublic
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, buDataStore
sınıfında birden fazlasetValue
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
veAnotherExample
sınıflarını (kök düzeyinde sınıflar olmaları durumunda) eşleştirmek ancakcom.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.