Uygulamanızı mümkün olduğunca küçük hale getirmek için sürüm derlemenizde kullanılmayan kod ve kaynakları kaldırmak üzere küçültmeyi etkinleştirmeniz gerekir. Daraltmayı etkinleştirdiğinizde, uygulamanızın sınıf ve üyelerinin adlarını kısaltan karartma ve uygulamanızın boyutunu daha da küçültmek için daha agresif stratejiler uygulayan optimizasyon özelliğinden de yararlanırsınız. Bu sayfada, R8'in projeniz için bu derleme süresi görevlerini nasıl özelleştirebileceğiniz ve bunları nasıl özelleştirebileceğiniz açıklanmaktadır.
Projenizi Android Gradle eklentisi 3.4.0 veya sonraki bir sürümü kullanarak oluşturduğunuzda, eklenti artık derleme zamanı kod optimizasyonu için ProGuard'ı kullanmaz. Eklenti, bunun yerine aşağıdaki derleme zamanı görevlerini işlemek için R8 derleyici ile çalışır:
- Kod küçültme (veya ağaç sallama): Kullanmadığınız sınıf, alan, yöntem ve özellikleri, uygulamanızın kitaplık bağımlılıklarını tespit edip güvenli bir şekilde kaldırır ve bu verileri 64 bin referans sınırını aşmak için değerli bir araç haline getirir. Örneğin, bir kitaplık bağımlılığının yalnızca birkaç API'sini kullanırsanız küçültme, uygulamanızın kullanmadığı kitaplık kodunu tanımlayabilir ve uygulamanızdan yalnızca bu kodu kaldırabilir. Daha fazla bilgi edinmek için kodunuzu küçültme bölümüne gidin.
- Kaynak daraltma: Paket uygulamanızdan, uygulamanızın kitaplık bağımlılıklarındaki kullanılmayan kaynaklar dahil olmak üzere kullanılmayan kaynakları kaldırır. Kod küçültme özelliğiyle birlikte çalışır. Böylece, kullanılmayan kod kaldırıldıktan sonra artık referans verilmeyen kaynaklar da güvenli bir şekilde kaldırılabilir. Daha fazla bilgi edinmek için kaynaklarınızı azaltma bölümüne gidin.
- Gizleme: Sınıfların ve üyelerin adını kısaltır. Bu da DEX dosya boyutlarının küçültülmesine neden olur. Daha fazla bilgi edinmek için kodunuzu karartma bölümüne gidin.
- Optimizasyon: Uygulamanızın DEX dosyalarının boyutunu daha da küçültmek için kodunuzu inceler ve yeniden yazar. Örneğin, R8, belirli bir if/else ifadesi için
else {}
şubesinin hiçbir zaman alınmadığını tespit ederseelse {}
dalına ait kodu kaldırır. Daha fazla bilgi edinmek için kod optimizasyonu ile ilgili bölüme gidin.
Uygulamanızın sürüm sürümünü oluştururken R8, yukarıda açıklanan derleme süresi görevlerini sizin için gerçekleştirecek şekilde yapılandırılabilir. Ayrıca, ProGuard kural dosyalarıyla belirli görevleri devre dışı bırakabilir veya R8'in davranışını özelleştirebilirsiniz. Aslında R8, mevcut tüm ProGuard kural dosyalarınızla çalışır. Bu nedenle, Android Gradle eklentisini R8 kullanacak şekilde güncellemek, mevcut kurallarınızı değiştirmenizi gerektirmemelidir.
Küçültmeyi, kod karartmayı ve optimizasyonu etkinleştirin
Android Studio 3.4 ya da Android Gradle eklentisi 3.4.0 ve sonraki sürümlerini kullandığınızda R8, projenizin Java bayt kodunu Android platformunda çalışan DEX biçimine dönüştüren varsayılan derleyicidir. Ancak Android Studio'yu kullanarak yeni bir proje oluşturduğunuzda küçültme, kod karartma ve kod optimizasyonu varsayılan olarak etkinleştirilmez. Bunun nedeni, bu derleme zamanı optimizasyonlarının projenizin derleme süresini artırmasıdır ve hangi kodu saklayacağınızı yeterince özelleştirmezseniz hatalara yol açabilir.
Bu nedenle, yayınlamadan önce test ettiğiniz son sürümünü oluştururken bu derleme süresi görevlerini etkinleştirmek en iyisidir. Küçültmeyi, kod karartmayı ve optimizasyonu etkinleştirmek için proje düzeyindeki derleme komut dosyanıza aşağıdakileri ekleyin.
Kotlin
android { buildTypes { getByName("release") { // Enables code shrinking, obfuscation, and optimization for only // your project's release build type. Make sure to use a build // variant with `isDebuggable=false`. isMinifyEnabled = true // Enables resource shrinking, which is performed by the // Android Gradle plugin. isShrinkResources = true // Includes the default ProGuard rules files that are packaged with // the Android Gradle plugin. To learn more, go to the section about // R8 configuration files. proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } ... }
Modern
android { buildTypes { release { // Enables code shrinking, obfuscation, and optimization for only // your project's release build type. Make sure to use a build // variant with `debuggable false`. minifyEnabled true // Enables resource shrinking, which is performed by the // Android Gradle plugin. shrinkResources true // Includes the default ProGuard rules files that are packaged with // the Android Gradle plugin. To learn more, go to the section about // R8 configuration files. proguardFiles getDefaultProguardFile( 'proguard-android-optimize.txt'), 'proguard-rules.pro' } } ... }
R8 yapılandırma dosyaları
R8, varsayılan davranışını değiştirmek ve uygulamanızın yapısını (örneğin, uygulamanızın koduna giriş noktası olarak işlev gören sınıflar) daha iyi anlamak için ProGuard kural dosyalarını kullanır. Bu kural dosyalarının bazılarını değiştirebilirsiniz ancak bazı kurallar, AAPT2 gibi derleme zamanı araçları tarafından otomatik olarak oluşturulabilir veya uygulamanızın kitaplık bağımlılıklarından devralınabilir. Aşağıdaki tabloda, R8'in kullandığı ProGuard kural dosyalarının kaynakları açıklanmaktadır.
Kaynak | Konum | Açıklama |
Android Studio | <module-dir>/proguard-rules.pro
|
Android Studio'yu kullanarak yeni bir modül oluşturduğunuzda, IDE bu modülün kök dizininde proguard-rules.pro dosyası oluşturur.
Varsayılan olarak, bu dosya herhangi bir kural için geçerli değildir. Bu nedenle, özel Keep kurallarınız gibi kendi ProGuard kurallarınızı buraya ekleyin. |
Android Gradle eklentisi | Derleme sırasında Android Gradle eklentisi tarafından oluşturulur. | Android Gradle eklentisi, çoğu Android projesi için faydalı olan kurallar içeren ve @Keep* ek açıklamalarını sağlayan proguard-android-optimize.txt oluşturur.
Varsayılan olarak, Android Studio'yu kullanarak yeni bir modül oluştururken modül düzeyindeki derleme komut dosyası, sizin için sürüm derlemenize bu kurallar dosyasını içerir.
Not: Android Gradle eklentisi, önceden tanımlanmış ek ProGuard kural dosyaları içerir ancak |
Kitaplık bağımlılıkları | AAR kitaplıkları: <library-dir>/proguard.txt
JAR kitaplıkları: |
AAR kitaplığı kendi ProGuard kural dosyasıyla yayınlanır ve bu AAR'yi derleme zamanı bağımlılığı olarak eklerseniz R8 projenizi derlerken kurallarını otomatik olarak uygular.
Kitaplığın düzgün çalışması için belirli Keep kuralları gerekliyse (yani kitaplık geliştiricisi sorun giderme adımlarını sizin için gerçekleştirdiyse), AAR kitaplıklarıyla paketlenmiş kural dosyalarının kullanılması yararlıdır. Bununla birlikte, ProGuard kuralları ekleyici olduğundan, AAR kitaplığı bağımlılığının içerdiği belirli kuralların kaldırılamayacağını ve uygulamanızın diğer bölümlerinin derlenmesini etkileyebileceğini unutmayın. Örneğin, bir kitaplık kod optimizasyonlarını devre dışı bırakan bir kural içeriyorsa bu kural projenizin tamamı için optimizasyonları devre dışı bırakır. |
Android Öğe Paketi Aracı 2 (AAPT2) | minifyEnabled true ile projenizi oluşturduktan sonra:
<module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt
|
AAPT2, uygulamanızın manifest dosyasındaki sınıflara, düzenlere ve diğer uygulama kaynaklarına yapılan referanslara dayalı olarak Keep kuralları oluşturur. Örneğin AAPT2, uygulamanızın manifest dosyasına giriş noktası olarak kaydettiğiniz her Etkinlik için bir saklama kuralı içerir. |
Özel yapılandırma dosyaları | Varsayılan olarak, Android Studio'yu kullanarak yeni bir modül oluşturduğunuzda IDE kendi kurallarınızı eklemeniz için <module-dir>/proguard-rules.pro oluşturur.
|
Ek yapılandırmalar dahil edebilirsiniz. R8, bunları derleme sırasında uygular. |
minifyEnabled
özelliğini true
olarak ayarladığınızda R8, yukarıda listelenen kullanılabilir tüm kaynaklardan gelen kuralları birleştirir. Kitaplık bağımlılıkları gibi diğer derleme süresi bağımlılıkları, R8 davranışında bilmediğiniz değişikliklere yol açabileceğinden R8 ile sorun giderirken bu bilgiyi aklınızda bulundurmanız önemlidir.
Projenizi oluştururken R8'in uyguladığı tüm kuralların tam bir raporunu almak için modülünüzün proguard-rules.pro
dosyasına aşağıdaki kodu ekleyin:
// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt
Ek yapılandırmaları dahil et
Android Studio'yu kullanarak yeni bir proje veya modül oluşturduğunuzda, IDE kendi kurallarınızı eklemeniz için bir <module-dir>/proguard-rules.pro
dosyası oluşturur. Diğer dosyalardan ek kuralları, modülünüzün derleme komut dosyasındaki proguardFiles
özelliğine ekleyerek de dahil edebilirsiniz.
Örneğin, her derleme varyantına özel kurallar eklemek için ilgili productFlavor
blokuna başka bir proguardFiles
özelliği ekleyebilirsiniz. Aşağıdaki Gradle dosyası, flavor2
ürün çeşidine flavor2-rules.pro
değerini ekler.
release
bloğundaki kurallar da uygulandığı için flavor2
artık üç ProGuard kuralını da kullanıyor.
Kotlin
android { ... buildTypes { getByName("release") { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), // List additional ProGuard rules for the given build type here. By default, // Android Studio creates and includes an empty rules file for you (located // at the root directory of each module). "proguard-rules.pro" ) } } flavorDimensions.add("version") productFlavors { create("flavor1") { ... } create("flavor2") { proguardFile("flavor2-rules.pro") } } }
Modern
android { ... buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), // List additional ProGuard rules for the given build type here. By default, // Android Studio creates and includes an empty rules file for you (located // at the root directory of each module). 'proguard-rules.pro' } } flavorDimensions "version" productFlavors { flavor1 { ... } flavor2 { proguardFile 'flavor2-rules.pro' } } }
Kodunuzu küçültme
minifyEnabled
özelliğini true
olarak ayarladığınızda R8 ile kod daraltma özelliği varsayılan olarak etkinleştirilir.
Kod küçültme (ağaç sallama olarak da bilinir), R8'in çalışma zamanında gerekli olmadığını belirlediği kodu kaldırma işlemidir. Örneğin, uygulamanız çok sayıda kitaplık bağımlılığı içeriyorsa ancak işlevlerinin yalnızca küçük bir kısmını kullanıyorsa bu işlem, uygulamanızın boyutunu önemli ölçüde azaltabilir.
Uygulamanızın kodunu daraltmak için R8 önce birleştirilmiş yapılandırma dosyaları grubuna göre uygulamanızın koduna tüm giriş noktalarını belirler. Bu giriş noktaları, Android platformunun uygulamanızın Etkinlikleri veya hizmetlerini açmak için kullanabileceği tüm sınıfları içerir. R8, her giriş noktasından başlayarak uygulamanızın çalışma zamanında erişebileceği tüm yöntemlerin, üye değişkenlerinin ve diğer sınıfların grafiğini oluşturmak için uygulamanızın kodunu inceler. Bu grafiğe bağlı olmayan koda ulaşılamaz ve uygulamadan kaldırılabilir.
Şekil 1'de, çalışma zamanı kitaplığı bağımlılığı olan bir uygulama gösterilmektedir. Uygulamanın kodunu incelerken R8, foo()
, faz()
ve bar()
yöntemlerine MainActivity.class
giriş noktasından erişilebildiğini belirler. Bununla birlikte, OkayApi.class
sınıfı veya baz()
yöntemi, çalışma zamanında uygulamanız tarafından kullanılmaz ve R8, uygulamanızı daraltırken bu kodu kaldırır.
Şekil 1. Derleme sırasında R8, erişilemeyen kodu belirlemek için projenizin birleştirilmiş Keep kurallarını temel alan bir grafik oluşturur.
R8, giriş noktalarını projenin R8 yapılandırma dosyalarındaki -keep
kuralları aracılığıyla belirler. Yani Keep kuralları, uygulamanızı daraltırken R8'in silmemesi gereken sınıfları belirtir ve R8 bu sınıfları uygulamanıza olası giriş noktaları olarak kabul eder. Android Gradle eklentisi ve AAPT2, sizin için çoğu uygulama projesinin gerektirdiği saklama kurallarını (ör. uygulamanızın etkinlikleri, görünümleri ve hizmetleri) otomatik olarak oluşturur. Bununla birlikte, bu varsayılan davranışı ek saklama kurallarıyla özelleştirmeniz gerekirse hangi kodun tutulacağını özelleştirme hakkındaki bölümü okuyun.
Bunun yerine yalnızca uygulamanızın kaynaklarının boyutunu küçültmek istiyorsanız kaynaklarınızı azaltma bölümüne geçin.
Saklanacak kodu özelleştirin
Çoğu durumda, varsayılan ProGuard kuralları dosyası (proguard-android-
optimize.txt
), R8'in yalnızca kullanılmayan kodu kaldırması için yeterlidir. Ancak bazı durumlarda R8'in doğru şekilde analiz etmesi zordur ve bu durum, uygulamanızın gerçekten ihtiyaç duyduğu kodu kaldırabilir. Aşağıda, kodun hatalı bir şekilde kaldırılabileceği durumlara dair bazı örnekler verilmiştir:
- Uygulamanız Java Yerel Arayüzünden (JNI) bir yöntemi çağırdığında
- Uygulamanız çalışma zamanında kod aradığında (ör. düşünme sırasında)
Uygulamanızı test etmek, uygunsuz bir şekilde kaldırılmış koddan kaynaklanan tüm hataları ortaya çıkarır. Ancak, kaldırılan kodla ilgili bir rapor oluşturarak da hangi kodun kaldırıldığını inceleyebilirsiniz.
Hataları düzeltmek ve R8'i belirli kodları tutmaya zorlamak için ProGuard kuralları dosyasına bir -keep
satırı ekleyin. Örneğin:
-keep public class MyClass
Alternatif olarak, saklamak istediğiniz koda @Keep
ek açıklamasını ekleyebilirsiniz. Bir sınıfa @Keep
eklendiğinde tüm sınıf olduğu gibi kalır. Bir yönteme veya alana eklediğinizde yöntem/alan (ve adı) ve sınıf adı aynı kalır. Bu ek açıklamanın yalnızca AndroidX Ek Açıklama Kitaplığı'nı
kullanırken ve Android Gradle eklentisi ile paketlenen ProGuard kuralları dosyasını eklediğinizde
daraltmayı etkinleştirme ile ilgili bölümde açıklandığı gibi
kullanılabildiğini unutmayın.
-keep
seçeneğini kullanırken dikkate almanız gereken birçok nokta vardır. Kural dosyanızı özelleştirmeyle ilgili daha fazla bilgi için ProGuard Kılavuzu'nu okuyun.
Sorun giderme bölümünde, kodunuz kaldırıldığında karşılaşabileceğiniz diğer yaygın sorunlar özetlenmiştir.
Yerel kitaplıkları çıkar
Yerel kod kitaplıkları, uygulamanızın sürüm derlemelerinde varsayılan olarak çıkarılır. Bu kaldırma işlemi, simge tablosunu kaldırıp uygulamanız tarafından kullanılan tüm yerel kitaplıklarda bulunan bilgilerin hatalarını ayıklama işlemini içerir. Yerel kod kitaplıklarının kaldırılması, önemli ölçüde boyut tasarrufu sağlar, ancak eksik bilgiler (sınıf ve işlev adları gibi) nedeniyle Google Play Console'da kilitlenmelerin teşhis edilmesi mümkün değildir.
Yerel kilitlenme desteği
Google Play Console, yerel kilitlenmeleri Android vitals altında bildirir. Birkaç adımda, uygulamanız için yerel hata ayıklama simgeleri dosyası oluşturup yükleyebilirsiniz. Bu dosya, üretim aşamasında uygulamanızın hatalarını ayıklamanıza yardımcı olmak için Android vitals'da simgeselleştirilmiş yerel kilitlenme yığın izlemelerini (sınıf ve işlev adlarını içerir) etkinleştirir. Bu adımlar, projenizde kullanılan Android Gradle eklentisinin sürümüne ve projenizin derleme çıkışına bağlı olarak değişir.
Android Gradle eklentisi 4.1 veya sonraki sürümleri
Projeniz Android App Bundle derliyorsa yerel hata ayıklama simgeleri dosyasını otomatik olarak buna ekleyebilirsiniz. Bu dosyayı sürüm derlemelerine eklemek için uygulamanızın build.gradle.kts
dosyasına aşağıdakileri ekleyin:
android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }
Aşağıdakiler arasından hata ayıklama sembolü düzeyini seçin:
- Play Console'un simgeselleştirilmiş yığın izlemelerinde işlev adlarını almak için
SYMBOL_TABLE
kullanın. Bu seviye tombstone'ları destekler. - Play Console'un simgesel yığın izlemelerinde işlev adlarını, dosyaları ve satır numaralarını almak için
FULL
kodunu kullanın.
Projeniz bir APK derliyorsa yerel hata ayıklama sembolleri dosyasını ayrı olarak oluşturmak için daha önce gösterilen build.gradle.kts
derleme ayarını kullanın. Yerel hata ayıklama simgeleri dosyasını Google Play Console'a manuel olarak yükleyin. Android Gradle eklentisi, derleme işleminin bir parçası olarak bu dosyayı aşağıdaki proje konumuna çıkarır:
app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip
Android Gradle eklentisi 4.0 veya önceki sürümler (ve diğer derleme sistemleri)
Android Gradle eklentisi, derleme işleminin bir parçası olarak sadeleştirilmemiş kitaplıkların kopyasını proje dizininde saklar. Bu dizin yapısı aşağıdakine benzer:
app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
├── arm64-v8a/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
├── x86/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
└── x86_64/
├── libgameengine.so
├── libothercode.so
└── libvideocodec.so
Bu dizinin içeriğini sıkıştırın:
cd app/build/intermediates/cmake/universal/release/obj
zip -r symbols.zip .
symbols.zip
dosyasını Google Play Console'a manuel olarak yükleyin.
Kaynaklarınızı azaltın
Kaynak daraltma, yalnızca kod daraltma işlemiyle birlikte çalışır. Kod küçültücü, kullanılmayan tüm kodları kaldırdıktan sonra kaynak küçültücü, uygulamanın hâlâ hangi kaynakları kullandığını belirleyebilir. Bu durum özellikle kaynak içeren kod kitaplıkları eklediğinizde geçerlidir. Kitaplık kaynaklarının başvurulmaması ve dolayısıyla kaynak küçültücü tarafından kaldırılması için kullanılmayan kitaplık kodunu kaldırmanız gerekir.
Kaynak daraltmayı etkinleştirmek için derleme komut dosyanızda shrinkResources
özelliğini true
olarak ayarlayın (kod daraltma için minifyEnabled
ile birlikte). Örneğin:
Kotlin
android { ... buildTypes { getByName("release") { isShrinkResources = true isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" ) } } }
Modern
android { ... buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
Kod daraltmak için uygulamanızı henüz minifyEnabled
kullanarak oluşturmadıysanız shrinkResources
özelliğini etkinleştirmeden önce bu işlemi deneyin. Çünkü kaynakları kaldırmaya başlamadan önce, dinamik olarak oluşturulan veya çağrılan sınıfları ya da yöntemleri korumak için proguard-rules.pro
dosyanızı düzenlemeniz gerekebilir.
Saklanacak kaynakları özelleştirin
Saklamak veya silmek istediğiniz belirli kaynaklar varsa projenizde <resources>
etiketiyle bir XML dosyası oluşturun ve tools:keep
özelliğinde tutulacak her bir kaynağı ve tools:discard
özelliğinde silinecek her kaynağı belirtin. Her iki özellik de virgülle ayrılmış bir kaynak adları listesini kabul eder. Yıldız karakterini joker karakter olarak kullanabilirsiniz.
Örneğin:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" />
Bu dosyayı proje kaynaklarınıza (ör. res/raw/keep.xml
) kaydedin. Derleme, bu dosyayı uygulamanıza paketlemez.
Silinecek kaynakları belirlemek size karmaşık gelebilir ancak bu yöntem, derleme varyantlarını kullanırken işinize yarayabilir. Örneğin, tüm kaynaklarınızı ortak proje dizinine yerleştirip belirli bir kaynağın kodda kullanıldığını (ve dolayısıyla küçültücü tarafından kaldırılmadığını) biliyorsanız ancak o kaynağın belirtilen derleme varyantı için kullanılmayacağını biliyorsanız her derleme varyantı için farklı bir keep.xml
dosyası oluşturabilirsiniz. Derleme araçlarının bir kaynağı gerektiği gibi yanlış bir şekilde tanımlaması da mümkündür. Bunun nedeni, derleyicinin kaynak kimliklerini satır içine eklemesi ve ardından kaynak analizcinin, kodda gerçekten başvuruda bulunulan bir kaynak ile kodda aynı değere sahip olan tam sayı değeri arasındaki farkı bilmemesidir.
Sıkı referans kontrollerini etkinleştir
Normalde kaynak küçültücü, bir kaynağın kullanılıp kullanılmadığını doğru bir şekilde belirleyebilir. Ancak kodunuz
Resources.getIdentifier()
için çağrı yapıyorsa (veya kitaplıklarınızdan herhangi biri bunu yapıyorsa AppCompat kitaplığı bunu yapıyorsa) kodunuz, dinamik olarak oluşturulan dizelere dayalı olarak kaynak adlarını arıyor demektir. Bunu yaptığınızda kaynak küçültücü varsayılan olarak savunmacı davranır ve eşleşen ad biçimine sahip tüm kaynakları potansiyel olarak kullanılıyor ve kaldırılmak üzere kullanılamıyor olarak işaretler.
Örneğin, aşağıdaki kod img_
ön ekine sahip tüm kaynakların kullanılmış olarak işaretlenmesine neden olur.
Kotlin
val name = String.format("img_%1d", angle + 1) val res = resources.getIdentifier(name, "drawable", packageName)
Java
String name = String.format("img_%1d", angle + 1); res = getResources().getIdentifier(name, "drawable", getPackageName());
Kaynak küçültücü aynı zamanda kodunuzdaki tüm dize sabitlerinin yanı sıra çeşitli res/raw/
kaynaklarını da inceleyerek file:///android_res/drawable//ic_plus_anim_016.png
benzeri bir biçimdeki kaynak URL'lerini arar. Araç, bunun gibi URL'ler oluşturmak için kullanılabilecek buna benzer dizeler bulursa bunları kaldırmaz.
Bunlar, varsayılan olarak etkin olan güvenli daraltma modu örnekleridir.
Ancak, bu "üzgünümden daha güvenli" işlemeyi kapatabilir ve kaynak küçültücüsünün yalnızca kullanıldığından emin olduğu kaynakları saklayacağını belirtebilirsiniz. Bunun için keep.xml
dosyasında shrinkMode
değerini strict
olarak ayarlayın:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:shrinkMode="strict" />
Katı daraltma modunu etkinleştirirseniz ve kodunuz, yukarıda gösterildiği gibi dinamik olarak oluşturulan dizelere sahip kaynaklara da referans veriyorsa tools:keep
özelliğini kullanarak bu kaynakları manuel olarak tutmanız gerekir.
Kullanılmayan alternatif kaynakları kaldırma
Gradle kaynak küçültücü yalnızca uygulama kodunuzun başvuruda bulunmadığı kaynakları kaldırır. Böylece farklı cihaz yapılandırmaları için
alternatif kaynakları kaldırmaz. Gerekirse uygulamanızın ihtiyaç duymadığı alternatif kaynak dosyalarını kaldırmak için Android Gradle eklentisinin resConfigs
özelliğini kullanabilirsiniz.
Örneğin, dil kaynakları içeren bir kitaplık (AppCompat veya Google Play Hizmetleri gibi) kullanıyorsanız uygulamanız, uygulamanızın geri kalanının aynı dillere çevrilmesine bakılmaksızın, bu kitaplıklardaki mesajlar için çevrilen tüm dil dizelerini içerir. Yalnızca uygulamanızın resmi olarak desteklediği dilleri korumak istiyorsanız bu dilleri resConfig
özelliğini kullanarak belirtebilirsiniz. Belirtilmemiş dillere ilişkin tüm kaynaklar kaldırılır.
Aşağıdaki snippet'te dil kaynaklarınızı yalnızca İngilizce ve Fransızca ile nasıl sınırlayacağınız gösterilmektedir:
Kotlin
android { defaultConfig { ... resourceConfigurations.addAll(listOf("en", "fr")) } }
Modern
android { defaultConfig { ... resConfigs "en", "fr" } }
Android App Bundle biçimini kullanarak bir uygulama yayınlarken, varsayılan olarak, uygulama yüklenirken yalnızca kullanıcının cihazında yapılandırılmış diller indirilir. Benzer şekilde, indirme işlemine yalnızca cihazın ekran yoğunluğuyla eşleşen kaynaklar ve cihazın ABI'siyle eşleşen yerel kitaplıklar dahil edilir. Daha fazla bilgi için Android Uygulama Paketi yapılandırması bölümüne bakın.
APK'larla yayınlanan (Ağustos 2021'den önce oluşturulmuş) eski uygulamalar için her biri farklı bir cihaz yapılandırmasını hedefleyen birden fazla APK oluşturarak APK'nıza hangi ekran yoğunluğunun veya ABI kaynaklarının dahil edileceğini özelleştirebilirsiniz.
Yinelenen kaynakları birleştirme
Varsayılan olarak Gradle, aynı şekilde adlandırılmış kaynakları (ör. farklı kaynak klasörlerinde olabilecek aynı ada sahip çizimler) da birleştirir. Bu davranış, shrinkResources
özelliği tarafından kontrol edilmez ve devre dışı bırakılamaz çünkü kodunuzun aradığı adla birden fazla kaynak eşleştiğinde hatalardan kaçınmak gerekir.
Kaynak birleştirme yalnızca iki veya daha fazla dosya aynı kaynak adını, türünü ve niteleyiciyi paylaştığında gerçekleşir. Gradle, kopyalar arasında en iyi seçenek olduğunu düşündüğü dosyayı seçer (aşağıda açıklanan öncelik sırasına göre) ve son yapıda dağıtılması için yalnızca bu kaynağı AAPT'ye aktarır.
Gradle aşağıdaki konumlarda yinelenen kaynaklar arıyor:
- Ana kaynak grubuyla ilişkilendirilmiş olan ana kaynaklar genellikle
src/main/res/
bölgesinde bulunur. - Derleme türü ve derleme çeşitlerinden varyantlar yer paylaşımlı olarak gösterilir.
- Kitaplık projesi bağımlılıkları.
Gradle, yinelenen kaynakları aşağıdaki basamaklı öncelik sırasına göre birleştirir:
Bağımlılıklar → Ana → Derleme türü → Derleme türü
Örneğin, hem ana kaynaklarınızda hem de derleme türünde yinelenen bir kaynak görünüyorsa Gradle derleme türündeki kaynağı seçer.
Aynı kaynaklar aynı kaynak kümesinde görünürse Gradle bunları birleştiremez ve kaynak birleştirme hatası verir. build.gradle.kts
dosyanızın sourceSet
özelliğinde birden çok kaynak grubu tanımlarsanız (örneğin, hem src/main/res/
hem de src/main/res2/
aynı kaynakları içeriyorsa) bu durum ortaya çıkabilir.
Kodunuzu karartın
Kod karartmanın amacı, uygulamanızdaki sınıf, yöntem ve alanların adlarını kısaltarak uygulamanızın boyutunu küçültmektir. Aşağıda, R8 kullanılarak kod karartma örneği verilmiştir:
androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
android.content.Context mContext -> a
int mListItemLayout -> O
int mViewSpacingRight -> l
android.widget.Button mButtonNeutral -> w
int mMultiChoiceItemLayout -> M
boolean mShowTitle -> P
int mViewSpacingLeft -> j
int mButtonPanelSideLayout -> K
Kod karartma, uygulamanızdan kodu kaldırmasa da birçok sınıf, yöntem ve alanı dizine ekleyen DEX dosyaları bulunan uygulamalarda önemli ölçüde boyut tasarrufu sağlanabilir. Bununla birlikte, kod karartma, kodunuzun farklı bölümlerini yeniden adlandırdığından yığın izlerini inceleme gibi belirli görevler için ek araçlar gerekir. Kod karartma işleminden sonra yığın izleme (stack trace) hakkında bilgi edinmek için karartılmış yığın izlemenin kodunu çözme konulu bölümü okuyun.
Buna ek olarak, kodunuz uygulamanızın yöntem ve sınıfları için tahmin edilebilir adlandırmaya dayanıyorsa (örneğin, yansıma özelliğini kullanırken) bu imzaları giriş noktası olarak değerlendirmeniz ve bunlar için saklama kuralları belirtmeniz gerekir. Bunun için, saklanacak kodu özelleştirme bölümündeki talimatları uygulamanız gerekir. Bu koruma kuralları, R8'e bu kodu uygulamanızın son DEX'inde tutmasını sağlamanın yanı sıra orijinal adlandırmasını da korur.
Kodu karartılmış yığın izlemenin kodunu çözme
R8, kodunuzda kod kararttıktan sonra sınıfların ve yöntemlerin adları değişmiş olabileceğinden, yığın izlemeyi anlamak zordur (ve imkansız değilse). Orijinal yığın izlemeyi (stack trace) elde etmek için yığın izlemeyi geri almanız gerekir.
Kod optimizasyonu
R8, uygulamanızı daha da küçültmek amacıyla, kullanılmayan daha fazla kodu kaldırmak için kodunuzu daha derin bir düzeyde inceler veya mümkünse kodunuzu daha az ayrıntılı olacak şekilde yeniden yazar. Aşağıda, bu tür optimizasyonlara ilişkin birkaç örnek verilmiştir:
- Kodunuz, belirli bir if/else ifadesi için
else {}
dalını hiçbir zaman almazsa R8,else {}
dalına ait kodu kaldırabilir. - Kodunuz bir yöntemi yalnızca tek bir yerde çağırırsa R8, yöntemi kaldırıp tek bir çağrı sitesinde satır içi olarak kullanabilir.
- R8, bir sınıfın yalnızca bir benzersiz alt sınıfı olduğunu belirlerse ve sınıfın kendisinin örneklenmez (örneğin, yalnızca bir somut uygulama sınıfı tarafından kullanılan soyut bir temel sınıf) R8, iki sınıfı birleştirip uygulamadan bir sınıfı kaldırabilir.
- Daha fazla bilgi edinmek için Jake Wharton'ın R8 optimizasyonu blog yayınlarını okuyun.
R8, ayrı optimizasyonları devre dışı bırakmanıza veya etkinleştirmenize ya da optimizasyonun davranışını değiştirmenize izin vermez. Aslında R8, -optimizations
ve -
optimizationpasses
gibi varsayılan optimizasyonları değiştirmeye çalışan ProGuard kurallarını yok sayar. Bu kısıtlama önemlidir, çünkü R8 gelişmeye devam ederken optimizasyonlar için standart bir davranış sürdürme, Android Studio ekibinin karşılaşabileceğiniz sorunları kolayca gidermesine ve çözmesine yardımcı olur.
Optimizasyonu etkinleştirmenin, uygulamanızın yığın izlemelerini değiştireceğini unutmayın. Örneğin, satır içine almak, yığın çerçevelerini kaldırır. Orijinal yığın izlemeleri (stack trace) elde etmeyi öğrenmek için geri çekme bölümüne bakın.
Daha agresif optimizasyonlar etkinleştirin
R8, ProGuard'dan farklı davranmasına neden olan bir dizi ek optimizasyon ("tam mod" olarak adlandırılır) içerir. Android Gradle eklentisi 8.0.0 sürümünden bu optimizasyonlar varsayılan olarak etkindir.
Projenizin gradle.properties
dosyasına aşağıdakileri ekleyerek bu ek optimizasyonları devre dışı bırakabilirsiniz:
android.enableR8.fullMode=false
Ek optimizasyonlar R8'in ProGuard'dan farklı davranmasına neden olduğundan, ProGuard için tasarlanmış kurallar kullanıyorsanız çalışma zamanı sorunlarını önlemek için ek ProGuard kuralları eklemeniz gerekebilir. Örneğin, kodunuzun Java Reffaction API aracılığıyla bir sınıfa referansta bulunduğunu varsayalım. "Tam mod"u kullanmadığında R8, çalışma zamanında söz konusu sınıfın nesnelerini (kodunuz gerçekten yapmasa bile) incelemek ve değiştirmek istediğinizi varsayar ve sınıfı ve statik başlatıcısını otomatik olarak korur.
Ancak, "tam mod" kullanılırken R8 bu varsayımda bulunmaz ve kodunuzun çalışma zamanında sınıfı hiçbir zaman kullanmadığını iddia ederse sınıfı uygulamanızın nihai DEX'inden kaldırır. Diğer bir deyişle, sınıfı ve statik başlatıcısını tutmak istiyorsanız bunu yapmak için kurallar dosyanıza bir Keep kuralı eklemeniz gerekir.
R8'in "tam modunu" kullanırken herhangi bir sorunla karşılaşırsanız olası bir çözüm için R8 SSS sayfasına bakın. Sorunu çözemiyorsanız lütfen hata bildiriminde bulunun.
Yığın izlerini geri çekme
R8 tarafından işlenen kod, yığın izlemeleri kaynak koduna tam olarak karşılık gelmeyeceğinden yığın izlemelerin anlaşılmasını zorlaştırabilecek çeşitli şekillerde değiştirilir. Bu durum, hata ayıklama bilgileri saklanmadığında satır numaralarındaki değişiklikler söz konusu olabilir. Bu durum, satır içine alma ve özetleme gibi optimizasyonlardan kaynaklanıyor olabilir. En çok katkıda bulunan unsur, sınıf ve yöntemlerin bile adları değiştireceği ve kod karartmadır.
R8, orijinal yığın izlemeyi (stack trace) kurtarmak için komut satırı araçları paketi ile birlikte sunulan retrace komut satırı aracını sağlar.
Uygulamanızın yığın izlemelerinin (stack trace) geri alınmasını desteklemek için, aşağıdaki kuralları modülünüzün proguard-rules.pro
dosyasına ekleyerek derlemenin geriye doğru izlenebilecek yeterli bilgiyi sakladığından emin olmanız gerekir:
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile
LineNumberTable
özelliği, bu konumların yığın izlemelere yazdırıldığı yöntemlerdeki konum bilgilerini tutar. SourceFile
özelliği, tüm potansiyel çalışma zamanlarının konum bilgilerini gerçekten yazdırmasını sağlar. -renamesourcefileattribute
yönergesi, yığın izlemelerdeki kaynak dosya adını yalnızca SourceFile
olarak ayarlar. Eşleme dosyası orijinal kaynak dosyayı içerdiği için geri alma sırasında gerçek orijinal kaynak dosya adı gerekli değildir.
R8, her çalıştırıldığında yığın izlerini orijinal yığın izleriyle eşlemek için gereken bilgileri içeren bir mapping.txt
dosyası oluşturur. Android Studio, dosyayı <module-name>/build/outputs/mapping/<build-type>/
dizinine kaydeder.
Uygulamanızı Google Play'de yayınlarken, uygulamanızın her sürümü için mapping.txt
dosyasını yükleyebilirsiniz. Android App Bundle'ı kullanarak yayınlarken bu dosya otomatik olarak uygulama paketi içeriğinin bir parçası olarak dahil edilir. Ardından Google Play, kullanıcı tarafından bildirilen sorunlardan gelen yığın izlemeleri yeniden izler. Böylece, bu sorunları Play Console'da inceleyebilirsiniz. Daha fazla bilgi için çökmeyle sonuçlanan yığın izlemelerin kodunu gösterme hakkındaki Yardım Merkezi makalesini inceleyin.
R8 ile sorun giderme
Bu bölümde, R8 kullanarak küçültme, kod karartma ve optimizasyonu etkinleştirirken karşılaşılan sorunları gidermeye yönelik bazı stratejiler açıklanmaktadır. Aşağıda sorununuza bir çözüm bulamazsanız R8 SSS sayfasını ve ProGuard'ın sorun giderme kılavuzunu da okuyun.
Kaldırılan (veya saklanan) kodla ilgili rapor oluşturma
Belirli R8 sorunlarını gidermenize yardımcı olması için R8'in uygulamanızdan kaldırdığı tüm kodların yer aldığı bir raporu görmek faydalı olabilir. Bu raporu oluşturmak istediğiniz her modül için özel kural dosyanıza -printusage <output-dir>/usage.txt
ekleyin. R8'i etkinleştirip uygulamanızı derlediğinizde R8, belirttiğiniz yol ve dosya adıyla bir rapor oluşturur. Kaldırılan kod raporu aşağıdakine benzer:
androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
public boolean hasWindowFeature(int)
public void setHandleNativeActionModesEnabled(boolean)
android.view.ViewGroup getSubDecor()
public void setLocalNightMode(int)
final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
private static final boolean DEBUG
private static final java.lang.String KEY_LOCAL_NIGHT_MODE
static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...
Bunun yerine , R8'in projenizin koruma kurallarından belirlediği giriş noktalarının raporunu görmek isterseniz özel kurallar dosyanıza -printseeds <output-dir>/seeds.txt
parametresini ekleyin. R8'i etkinleştirdiğinizde ve uygulamanızı derlediğinizde R8, belirttiğiniz yol ve dosya adını içeren bir rapor oluşturur. Saklanan giriş noktalarının raporu aşağıdakine benzer:
com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...
Kaynak daraltma sorunlarını giderme
Kaynakları küçülttüğünüzde, Derleme
penceresinde uygulamadan kaldırılan kaynakların bir özeti gösterilir. (Önce Gradle'dan ayrıntılı metin çıkışını görüntülemek için önce pencerenin sol tarafındaki Görünümü aç'ı
tıklamanız gerekir.) Örneğin:
:android:shrinkDebugResources
Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning
Gradle, <module-name>/build/outputs/mapping/release/
içinde resources.txt
adlı bir teşhis dosyası da oluşturur (ProGuard'ın çıkış dosyalarıyla aynı klasör). Bu dosya, hangi kaynakların diğer kaynaklara başvurduğu ve hangi kaynakların kullanıldığı veya kaldırıldığı gibi ayrıntıları içerir.
Örneğin, @drawable/ic_plus_anim_016
uygulamasının neden hâlâ uygulamanızda olduğunu öğrenmek için resources.txt
dosyasını açıp söz konusu dosya adını arayın. Aşağıdaki gibi başka bir kaynaktan bu öğeye referans verildiğini görebilirsiniz:
16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out] @drawable/ic_plus_anim_016
Artık @drawable/add_schedule_fab_icon_anim
kaynağına neden ulaşılabildiğini bilmeniz gerekiyor. Yukarı doğru arama yaptığınızda bu kaynağın "Erişilebilir kök kaynaklar:" bölümünde listelendiğini görürsünüz. Bu, add_schedule_fab_icon_anim
için bir kod referansı olduğu anlamına gelir (yani, R.drawable kimliği erişilebilir kodda bulunmuştur).
Sıkı kontrol kullanmıyorsanız kaynak kimlikleri, dinamik olarak yüklenen kaynaklar için kaynak adları oluşturmak amacıyla kullanılabilmiş gibi görünen dize sabitleri varsa erişilebilir olarak işaretlenebilir. Bu durumda, derleme çıktısında kaynak adını ararsanız şuna benzer bir mesaj bulabilirsiniz:
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
used because it format-string matches string pool constant ic_plus_anim_%1$d.
Bu dizelerden birini görürseniz ve dizenin, belirtilen kaynağı dinamik olarak yüklemek için kullanılmadığından eminseniz derleme sistemini kaldırması için bilgi vermek üzere tools:discard
özelliğini kullanabilirsiniz. Bu bilgi, saklanacak kaynakları özelleştirme hakkındaki bölümde açıklanmıştır.