Uygulamanızı küçültme, gizleme ve optimize etme

Uygulamanızı mümkün olduğunca küçük ve hızlı hale getirmek için sürüm derlemenizi isMinifyEnabled = true ile optimize etmeli ve küçültmelisiniz.

Böylece, kullanılmayan kod ve kaynakları kaldıran küçültme, uygulamanızın sınıflarının ve üyelerinin adlarını kısaltan karartma ve uygulamanızın boyutunu daha da küçültmek ve performansını iyileştirmek için daha agresif stratejiler uygulayan optimizasyon mümkün olur. Bu sayfada, R8'in projenizde bu derleme zamanı görevlerini nasıl gerçekleştirdiği ve bunları nasıl özelleştirebileceğiniz açıklanmaktadır.

Projenizi Android Gradle eklentisi 3.4.0 veya sonraki bir sürümünü kullanarak oluşturduğunuzda, eklenti artık derleme zamanı kod optimizasyonu yapmak için ProGuard'ı kullanmaz. Bunun yerine, eklenti aşağıdaki derleme zamanı görevlerini işlemek için R8 derleyici ile çalışır:

  • Kod küçültme (veya ağaç sallama): Kullanılmayan sınıfları, alanları, yöntemleri ve özellikleri uygulamanızdan ve kitaplığındaki bağımlılıklardan tespit edip güvenli bir şekilde kaldırır. Bu da 64 bin referans sınırını aşmak için değerli bir araç haline gelir. Örneğin, bir kitaplık bağımlılığının yalnızca birkaç API'sini kullanıyorsanız daraltma işlemi, uygulamanızın kullanmadığı kitaplık kodunu belirleyebilir ve uygulamanızdan yalnızca bu kodu kaldırabilir. Daha fazla bilgi edinmek için kodunuzu küçültme ile ilgili bölüme gidin.
  • Kaynak azaltma: Uygulamanızın kitaplığı bağımlılıklarındaki kullanılmayan kaynaklar da dahil olmak üzere kullanılmayan kaynakları paket uygulamanızdan kaldırır. Kod küçültme ile birlikte çalışır. Böylece, kullanılmayan kod kaldırıldığında, artık başvuruda bulunmayan kaynaklar da güvenli bir şekilde kaldırılabilir. Daha fazla bilgi edinmek için kaynaklarınızı azaltma ile ilgili bölüme gidin.
  • Optimizasyon: Çalışma zamanı performansını iyileştirmek ve uygulamanızın DEX dosyalarının boyutunu daha da küçültmek için kodunuzu inceler ve yeniden yazar. Bu, kodun çalışma zamanı performansını %30'a kadar artırarak başlatma ve kare zamanlamasını büyük ölçüde iyileştirir. Örneğin, R8, belirli bir if/else ifadesi için else {} kolunun hiç alınmadığını tespit ederse R8, else {} dalının kodunu kaldırır. Daha fazla bilgi edinmek için kod optimizasyonu bölümüne gidin.
  • Görselleştirme (veya tanımlayıcı küçültme): Sınıfların ve üyelerin adını kısaltarak DEX dosya boyutlarını küçültür. Daha fazla bilgi edinmek için kodunuzu karartma ile ilgili bölüme gidin.

R8, uygulamanızın sürüm sürümünü oluştururken yukarıda açıklanan derleme süresi görevlerini sizin için gerçekleştirecek şekilde yapılandırılabilir. Ayrıca belirli görevleri devre dışı bırakabilir veya ProGuard kural dosyaları aracılığıyla 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.

Daraltmayı, kod karartmayı ve optimizasyonu etkinleştirme

Android Studio 3.4 veya 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 daraltma, kod karartma ve kod optimizasyonu varsayılan olarak etkin değildir. Bunun nedeni, bu derleme zamanı optimizasyonlarının projenizin derleme süresini artırması ve hangi kodun saklanacağını yeterince özelleştirmezseniz hatalara neden olabilmesidir.

Bu nedenle, uygulamanızın yayınlamadan önce test ettiğiniz son sürümünü oluştururken bu derleme zamanı görevlerini etkinleştirmek en iyi seçenektir. Daraltmayı, kod karartmayı ve optimizasyonu etkinleştirmek için proje düzeyinde 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

            proguardFiles(
                // 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.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // Includes a local, custom Proguard rules file
                "proguard-rules.pro"
            )
        }
    }
    ...
}

Eski

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ı (ör. uygulamanızın koduna giriş noktaları 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ştirebilseniz de 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, söz konusu modülün kök dizininde bir proguard-rules.pro dosyası oluşturur.

Varsayılan olarak bu dosya herhangi bir kural uygulamaz. Bu nedenle, özel saklama kurallarınız gibi kendi ProGuard kurallarınızı buraya ekleyin.

Android Gradle eklentisi Derleme sırasında Android Gradle eklentisi tarafından oluşturuldu. Android Gradle eklentisi, çoğu Android projesi için yararlı olan kuralları içeren ve @Keep* ek açıklamaları etkinleştiren 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ı, varsayılan olarak sürüm derlemenize bu kural dosyasını dahil eder.

Not: Android Gradle eklentisi önceden tanımlanmış ek ProGuard kural dosyaları içerir ancak proguard-android-optimize.txt kullanmanız önerilir.

Kitaplık bağımlılıkları AAR kitaplıkları: <library-dir>/proguard.txt

JAR kitaplıkları: <library-dir>/META-INF/proguard/

Bir AAR kitaplığı kendi ProGuard kural dosyasıyla yayınlanırsa ve bu AAR'yı derleme süresi bağımlılığı olarak eklerseniz R8, projenizi derlerken kurallarını otomatik olarak uygular.

AAR kitaplıklarıyla paketlenen kural dosyalarının kullanılması, kitaplığın düzgün çalışması için belirli saklama kurallarının gerekli olması durumunda (yani kitaplık geliştiricisi sizin için sorun giderme adımlarını uyguladıysa) kullanışlıdır.

Bununla birlikte, ProGuard kuralları ekleyici olduğundan, bir 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ırakmaya dair bir kural içeriyorsa bu kural, projenizin tamamında optimizasyonları devre dışı bırakır.

Android Öğe Paketi Aracı 2 (AAPT2) Projenizi minifyEnabled true ile oluşturduktan sonra: <module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt AAPT2 uygulamanızın manifest dosyasındaki, düzenleri ve diğer uygulama kaynaklarındaki sınıflara yapılan referanslara dayalı olarak saklama 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 ve R8, bunları derleme sırasında uygular.

minifyEnabled özelliğini true olarak ayarladığınızda R8, yukarıda listelenen tüm kullanılabilir 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 neden olabileceğinden R8 ile ilgili sorunları giderirken bunu aklınızda bulundurmanız önemlidir.

Projenizi oluştururken R8'in geçerli olduğu tüm kuralların tam kapsamlı bir raporunun çıktısını almak için modülünüzün proguard-rules.pro dosyasına aşağıdakileri 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. Ayrıca, diğer dosyaları modülünüzün derleme komut dosyasındaki proguardFiles özelliğine ekleyerek diğer dosyalardan ek kurallar da dahil edebilirsiniz.

Örneğin, karşılık gelen productFlavor bloğuna başka bir proguardFiles özelliği ekleyerek her derleme varyantına özel kurallar ekleyebilirsiniz. Aşağıdaki Gradle dosyası flavor2 ürün aromasına flavor2-rules.pro ekler. Artık flavor2, release bloğundaki kurallar da uygulandığı için üç ProGuard kuralının tümünü kullanıyor.

Ayrıca, yalnızca test APK'sında yer alan ProGuard dosyalarının bir listesini belirten testProguardFiles özelliğini ekleyebilirsiniz:

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"
            )
            testProguardFiles(
                // The proguard files listed here are included in the
                // test APK only.
                "test-proguard-rules.pro"
            )
        }
    }
    flavorDimensions.add("version")
    productFlavors {
        create("flavor1") {
            ...
        }
        create("flavor2") {
            proguardFile("flavor2-rules.pro")
        }
    }
}

Eski

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'
            testProguardFiles
                // The proguard files listed here are included in the
                // test APK only.
                'test-proguard-rules.pro'
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

Kodunuzu küçültün

minifyEnabled özelliğini true olarak ayarladığınızda R8 ile kod daraltma 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 sürecidir. Örneğin, uygulamanız çok sayıda kitaplık bağımlılığı içermesine rağmen işlevlerinin yalnızca küçük bir kısmını kullanıyorsa bu işlem uygulamanızın boyutunu önemli ölçüde küçültebilir.

R8, uygulamanızın kodunu küçültmek için öncelikle birleştirilmiş yapılandırma dosyaları grubuna dayanarak uygulamanızın koduna tüm giriş noktalarını belirler. Bu giriş noktaları, Android platformunun, uygulamanızın Etkinliklerini 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 kodunu inceleyerek 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 bir grafiğini oluşturur. Söz konusu grafiğe bağlı olmayan kod, ulaşılamaz olarak kabul edilir ve uygulamadan kaldırılabilir.

Şekil 1'de çalışma zamanı kitaplığı bağımlılığı olan bir uygulama gösterilmektedir. R8, uygulamanın kodunu incelerken foo(), faz() ve bar() yöntemlerine MainActivity.class giriş noktasından ulaşılabileceğini belirler. Ancak OkayApi.class sınıfı veya baz() yöntemi, çalışma zamanında hiçbir zaman uygulamanız tarafından kullanılmaz ve R8, uygulamanızı küçültürken bu kodu kaldırır.

Şekil 1. Derleme sırasında R8, erişilemeyen kodu belirlemek için projenizin birleşik tutma kurallarına dayalı bir grafik oluşturur.

R8, projenin R8 yapılandırma dosyalarındaki -keep kuralları aracılığıyla giriş noktalarını belirler. Yani tutma kuralları, uygulamanızı küçültü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 çoğu uygulama projesinin gerektirdiği saklama kurallarını (uygulamanızın etkinlikleri, görünümleri ve hizmetleri gibi) otomatik olarak oluşturur. Bununla birlikte, bu varsayılan davranışı ek saklama kurallarıyla özelleştirmeniz gerekiyorsa hangi kodun saklanacağını özelleştirme ile ilgili bölümü okuyun.

Bunun yerine yalnızca uygulamanızın kaynaklarının boyutunu küçültmek istiyorsanız kaynaklarınızı azaltma ile ilgili bölüme geçin.

Bir kitaplık projesi küçültüldüyse bu kitaplığı kullanan bir uygulamanın, daraltılmış kitaplık sınıflarını içereceğini unutmayın. Kitaplık APK'sında eksik sınıflar varsa kitaplık saklama kurallarını ayarlamanız gerekebilir. AAR biçiminde bir kitaplık oluşturup yayınlıyorsanız kitaplığınızın temel aldığı yerel JAR dosyaları AAR dosyasına daraltılmaz.

Saklanacak kodu özelleştirin

Çoğu durumda, R8'in yalnızca kullanılmayan kodu kaldırması için varsayılan ProGuard kuralları dosyası (proguard-android- optimize.txt) yeterlidir. Ancak bazı durumları R8'in doğru şekilde analiz etmesi zordur ve uygulamanızın gerçekten ihtiyaç duyduğu kodu kaldırabilir. Kodun hatalı bir şekilde kaldırılabileceği durumlara örnek olarak şunlar verilebilir:

  • Uygulamanız Java Yerel Arayüzü'nden (JNI) bir yöntem çağırdığında
  • Uygulamanız çalışma zamanında kod aradığında (ör. yansıma sırasında)

Uygulamanızı test etmek, uygunsuz bir şekilde kaldırılan kodun neden olduğu hataları ortaya çıkarır. Bununla birlikte, kaldırılan kod raporu oluşturarak hangi kodun kaldırıldığını de inceleyebilirsiniz.

Hataları düzeltmek ve R8'i belirli kodları saklamaya zorlamak için ProGuard kuralları dosyasına bir -keep satırı ekleyin. Örnek:

-keep public class MyClass

Alternatif olarak, saklamak istediğiniz koda @Keep ek açıklamasını ekleyebilirsiniz. Bir sınıfa @Keep eklenmesi tüm sınıfın olduğu gibi kalmasını sağlar. Bir yönteme veya alana eklendiğinde, yöntem/alan (ve adı) ile sınıf adı korunur. Bu ek açıklamanın, küçültmeyi etkinleştirme ile ilgili bölümde açıklandığı gibi yalnızca AndroidX Ek Açıklama Kitaplığı'nı kullanırken ve Android Gradle eklentisiyle birlikte gelen ProGuard kuralları dosyasını dahil ettiğinizde kullanılabileceğini unutmayın.

-keep seçeneğini kullanırken dikkat etmeniz gereken birçok nokta vardır. Kurallar dosyanızı özelleştirmeyle ilgili daha fazla bilgi için ProGuard Kılavuzu'nu okuyun. Sorun giderme bölümünde, kodunuz çıkarıldığında karşılaşabileceğiniz diğer yaygın sorunlar özetlenmektedir.

Yerel kitaplıkları çıkar

Varsayılan olarak, yerel kod kitaplıkları, uygulamanızın sürüm derlemelerinden çıkarılır. Bu çıkarma işlemi, simge tablosunu kaldırmak ve uygulamanızın kullandığı yerel kitaplıklarda bulunan hata ayıklama bilgilerini kaldırmaktan oluşur. Yerel kod kitaplıklarının çıkarılması büyük boyutlu tasarruf sağlar, ancak Google Play Console'daki kilitlenmeleri eksik bilgiler (sınıf ve işlev adları gibi) nedeniyle teşhis etmek mümkün değildir.

Yerel kilitlenme desteği

Google Play Console, yerel kilitlenmeleri Android vitals altında raporlar. Birkaç adımda, uygulamanız için bir yerel hata ayıklama simgeleri dosyası oluşturup yükleyebilirsiniz. Bu dosya, üretimde uygulamanızda hata ayıklamanıza yardımcı olmak için Android vitals'da simgeselleştirilmiş yerel kilitlenmeyle ilgili yığın izlemelere (sınıf ve işlev adlarını içeren) olanak tanır. Bu adımlar, projenizde kullanılan Android Gradle eklentisinin sürümüne ve projenizin derleme çıkışına bağlı olarak değişiklik gösterir.

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 dahil etmek için uygulamanızın build.gradle.kts dosyasına aşağıdakini ekleyin:

android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }

Aşağıdakilerden 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 düzey mezar taşlarını destekler.
  • Play Console'un sembolik yığın izlemelerinde işlev adlarını, dosyaları ve satır numaralarını almak için FULL kullanın.

Projeniz bir APK derliyorsa yerel hata ayıklama simgeleri 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ı şuna 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
  1. Bu dizinin içeriğini zip dosyası olarak kaydedin:

    cd app/build/intermediates/cmake/universal/release/obj
    zip -r symbols.zip .
    
  2. symbols.zip dosyasını Google Play Console'a manuel olarak yükleyin.

Kaynaklarınızı küçültün

Kaynak daraltma yalnızca kod daraltma ile birlikte çalışır. Kod daraltıcı, kullanılmayan tüm kodları kaldırdıktan sonra, kaynak daraltı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ına referans verilmemesi için kullanılmayan kitaplık kodunu kaldırmanız gerekir. Böylece, kaynak daraltıcı tarafından kaldırılabilir.

Kaynak daraltmayı etkinleştirmek için derleme komut dosyanızda shrinkResources özelliğini true olarak ayarlayın (kod küçültme için minifyEnabled ile birlikte). Örnek:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isShrinkResources = true
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

Eski

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

Uygulamanızı henüz kod küçültme için minifyEnabled kullanarak derlemediyseniz shrinkResources etkinleştirmeden önce bu işlemi deneyin. Kaynakları kaldırmaya başlamadan önce, dinamik olarak oluşturulan veya çağrılan sınıfları ya da yöntemleri saklamak için proguard-rules.pro dosyanızı düzenlemeniz gerekebilir.

Saklanacak kaynakları özelleştirin

Tutmak veya silmek istediğiniz belirli kaynaklar varsa projenizde <resources> etiketiyle bir XML dosyası oluşturun ve tools:keep özelliğinde tutulacak her kaynağı ve tools:discard özelliğinde silinecek her kaynağı belirtin. Her iki özellik de kaynak adlarının virgülle ayrılmış listesini kabul eder. Yıldız karakterini joker olarak kullanabilirsiniz.

Örnek:

<?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.

Hangi kaynakların silineceğini belirtmek, bu kaynakları silmek yerine silineceği zaman saçma gelebilir, ancak derleme varyantları kullanılırken faydalı olabilir. Örneğin, tüm kaynaklarınızı ortak proje dizinine yerleştirebilir, daha sonra belirli bir kaynağın kodda kullanıldığını (ve dolayısıyla da daraltıcı tarafından kaldırılmadığını) bildiğinizde ancak belirtilen derleme varyantı için kullanılmayacağını bildiğinizde her derleme varyantı için farklı bir keep.xml dosyası oluşturabilirsiniz. Derleme araçlarının bir kaynağı gerektiği gibi yanlış tanımlaması da mümkündür. Bu durum, derleyicinin kaynak kimliklerini satır içine eklemesinden ve kaynak analizcisinin, gerçekten referans verilen bir kaynak ile kodda aynı değere sahip olan bir tam sayı değeri arasındaki farkı bilmemesinden kaynaklanabilir.

Katı referans kontrollerini etkinleştir

Normalde, kaynak daraltıcı bir kaynağın kullanılıp kullanılmadığını doğru bir şekilde belirleyebilir. Bununla birlikte, kodunuz Resources.getIdentifier() çağrısı yapıyorsa (veya kitaplıklarınızdan herhangi biri bunu AppCompat kitaplığı yapıyorsa) yapıyorsa kodunuz dinamik olarak oluşturulan dizelere göre kaynak adlarını aramaktadır. Bunu yaptığınızda, kaynak küçültücü varsayılan olarak savunma amaçlı davranır ve eşleşen bir ad biçimine sahip tüm kaynakları "potansiyel olarak kullanılıyor ve kaldırılamaz" olarak işaretler.

Örneğin, aşağıdaki kod, img_ ön ekine sahip tüm kaynakların "kullanıldı" 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 daraltıcı, kodunuzdaki tüm dize sabitlerine ve çeşitli res/raw/ kaynaklarına da göz atarak file:///android_res/drawable//ic_plus_anim_016.png benzeri bir biçimdeki kaynak URL'lerini arar. Bunun gibi URL'ler oluşturmak için kullanılabilecekmiş gibi görünen dizeler veya bunun gibi başka dizeler bulursa, bunları kaldırmaz.

Bunlar, varsayılan olarak etkinleştirilen güvenli küçültme moduna örneklerdir. Bununla birlikte, bu "üzgünümden daha güvenli" işlemeyi kapatabilir ve kaynak daraltıcının yalnızca kullanıldığından emin olduğu kaynakları tutacağını belirtebilirsiniz. Bunu yapmak için keep.xml dosyasında shrinkMode öğesini strict olarak ayarlayın. Örneğin:

<?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 referansta bulunursa bu kaynakları tools:keep özelliğini kullanarak manuel olarak tutmanız gerekir.

Kullanılmayan alternatif kaynakları kaldırın

Gradle kaynak daraltıcı, yalnızca uygulama kodunuz tarafından başvuruda bulunulmayan 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ını (ör. AppCompat veya Google Play Hizmetleri) içeren bir kitaplık kullanıyorsanız uygulamanızın geri kalanının aynı dillere çevrilse de çevrilmemesinden bağımsız olarak uygulamanız bu kitaplıklardaki mesajlar için çevrilmiş tüm dil dizelerini içerir. Yalnızca uygulamanızın resmi olarak desteklediği dilleri tutmak isterseniz bu dilleri resConfig özelliğini kullanarak belirtebilirsiniz. Belirtilmeyen diller için kaynaklar kaldırılır.

Aşağıdaki snippet'te dil kaynaklarınızı yalnızca İngilizce ve Fransızca olarak nasıl sınırlayacağınız gösterilmektedir:

Kotlin

android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}

Eski

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

Bir uygulama, Android App Bundle biçimini kullanarak yayınlarken varsayılan olarak yalnızca kullanıcının cihazında yapılandırılan diller indirilir. Benzer şekilde, yalnızca cihazın ekran yoğunluğuyla eşleşen kaynaklar ve cihazın ABI'siyle eşleşen yerel kitaplıklar da indirme işlemine dahil edilir. Daha fazla bilgi için Android App Bundle yapılandırmasına göz atı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 dahil edilecek ekran yoğunluğunu veya ABI kaynaklarını özelleştirebilirsiniz.

Yinelenen kaynakları birleştirme

Varsayılan olarak Gradle, farklı kaynak klasörlerinde bulunabilecek aynı ada sahip çekinceler gibi aynı şekilde adlandırılmış kaynakları da birleştirir. Bu davranış, shrinkResources özelliği tarafından kontrol edilmez ve devre dışı bırakılamaz. Çünkü birden fazla kaynak, kodunuzun aradığı adla eşleştiğinde hatalardan kaçınmak gerekir.

Kaynak birleştirme, yalnızca iki veya daha fazla dosyanın aynı kaynak adını, türünü ve niteleyiciyi paylaştığında gerçekleşir. Gradle, yinelenenler arasında hangi dosyayı en iyi seçenek olarak değerlendireceğini seçer (aşağıda açıklanan öncelik sırasına göre) ve nihai yapıda dağıtım için yalnızca bir kaynağı AAPT'ye iletir.

Gradle, aşağıdaki konumlarda yinelenen kaynaklar olup olmadığını kontrol eder:

  • Ana kaynak grubuyla ilişkili ana kaynaklar genellikle src/main/res/ konumunda bulunur.
  • Derleme türü ve derleme çeşitlerine ait varyant yer paylaşımları.
  • Kitaplık projesi bağımlılıkları.

Gradle, yinelenen kaynakları aşağıdaki kademeli ö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ünden olanı seçer.

Aynı kaynak grubunda aynı kaynaklar görünüyorsa Gradle bunları birleştiremez ve kaynak birleştirme hatası verir. Bu durum, build.gradle.kts dosyanızın sourceSet özelliğinde birden fazla kaynak kümesi tanımlarsanız (örneğin, hem src/main/res/ hem de src/main/res2/ aynı kaynakları içeriyorsa) bu durum ortaya çıkabilir.

Kodunuzu karartma

Kod karartma işleminin amacı, uygulamanızdaki sınıfların, yöntemlerin ve alanların adlarını kısaltarak uygulamanızın boyutunu küçültmektir. Aşağıda, R8'in kullanıldığı bir 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

Karartma işlemi kodu uygulamanızdan kaldırmaz ancak birçok sınıfı, yöntemi ve alanı dizine ekleyen DEX dosyaları içeren uygulamalarda önemli ölçüde boyut tasarrufu görülebilir. Ancak kod karartma, kodunuzun farklı bölümlerini yeniden adlandırdığından yığın izlemeleri inceleme gibi belirli görevler için ek araçlar gerekir. Kod karartma işleminden sonra yığın izlemenizi anlamak için karartılmış yığın izlemenin kodunu çözme ile ilgili bölümü okuyun.

Ayrıca, kodunuz uygulamanızın yöntemleri ve sınıfları için tahmin edilebilir adlandırmaya dayanıyorsa (örneğin, yansıtma kullanırken bu imzaları giriş noktaları olarak ele almalı ve hangi kodun saklanacağını özelleştirme ile ilgili bölümde açıklandığı gibi bunlar için saklama kuralları belirtmeniz gerekir). Bu saklama kuralları R8'e, bu kodu hem uygulamanızın son DEX'inde tutmasını hem de orijinal adını korumasını bildirir.

Kodu karartılmış yığın izlemenin kodunu çözme

R8, kodunuzu kararttıktan sonra, sınıfların ve yöntemlerin adları değişmiş olabileceğinden, yığın izlemeyi anlamak zordur (imkansız değilse). Orijinal yığın izlemeyi (stack trace) edinmek için yığın izlemeyi geri almanız gerekir.

Kod optimizasyonu

R8, uygulamanızı daha da optimize etmek için, kullanılmayan kodları kaldırmak veya mümkün olduğunda kodunuzu daha az ayrıntı içerecek şekilde yeniden yazmak amacıyla kodunuzu daha ayrıntılı bir şekilde inceler. 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ının kodunu kaldırabilir.
  • Kodunuz yalnızca birkaç yerde bir yöntem çağırıyorsa R8, yöntemi kaldırıp birkaç çağrı sitesinde satır içi yapabilir.
  • R8 bir sınıfın yalnızca bir benzersiz alt sınıfa sahip olduğunu ve sınıfın örneklenmediğini (örneğin, yalnızca bir somut uygulama sınıfı tarafından kullanılan soyut bir temel sınıf) belirlerse R8, iki sınıfı birleştirebilir ve bir sınıfı uygulamadan 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 bir optimizasyonun davranışını değiştirmenize izin vermez. Hatta R8, -optimizations ve -optimizationpasses gibi varsayılan optimizasyonları değiştirmeye çalışan tüm ProGuard kurallarını yok sayar. Bu kısıtlama önemlidir çünkü R8 iyileşmeye devam ederken optimizasyonlar için standart bir davranış sürdürülmesi, Android Studio ekibinin karşılaşabileceğiniz sorunları kolayca gidermesine ve çözmesine yardımcı olur.

Optimizasyonu etkinleştirdiğinizde, uygulamanızın yığın izlemeleri (stack trace) değişir. Örneğin, satır içine almak yığın çerçevelerini kaldırır. Orijinal yığın izlemeleri nasıl edineceğinizi öğrenmek için geri izleme ile ilgili bölüme bakın.

Çalışma zamanı performansı üzerindeki etkisi

Daraltma, kod karartma ve optimizasyonun tümü etkinse R8, kodun çalışma zamanı performansını (kullanıcı arayüzü iş parçacığında başlatma ve kare süresi dahil) %30'a kadar iyileştirir. Bunlardan herhangi birini devre dışı bırakmak, R8'in kullandığı optimizasyon grubunu büyük ölçüde sınırlandırır.

R8 etkinleştirilirse daha da iyi başlangıç performansı için Başlangıç Profilleri oluşturmanız da gerekir.

Daha agresif optimizasyonları etkinleştirin

R8, bir dizi ek optimizasyon ("tam mod" olarak adlandırılır) içerir. Bu da, ProGuard'dan farklı davranışlara neden olur. Bu optimizasyonlar Android Gradle eklentisi 8.0.0 sürümünden itibaren 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ı eklemenizi gerektirebilir. Örneğin, kodunuzun Java Reflection API aracılığıyla bir sınıfa başvurduğunu düşünelim. "Tam mod" kullanılmadığında R8, kodu gerçekten kullanmasa bile çalışma zamanında ilgili sınıfın nesnelerini incelemeyi ve işlemeyi planladığınızı varsayar ve sınıfı ve statik başlatıcıyı otomatik olarak korur.

Ancak "tam mod" kullanılırken R8 bu varsayımda bulunmaz ve R8, kodunuzun çalışma zamanında sınıfı hiçbir zaman kullanmadığını iddia ederse, uygulamanızın son DEX'inden kaldırılır. Yani sınıfı ve statik başlatıcıyı korumak 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 çözemezseniz lütfen hata bildiriminde bulunun.

Yığın izlemeyi geri çekme

R8 tarafından işlenen kodların farklı şekillerde değiştirilmesi, yığın izlemelerin anlaşılmasını zorlaştırabilir. Bunun nedeni, yığın izlemelerin kaynak koda tam olarak karşılık gelmemesidir. Hata ayıklama bilgileri saklanmadığında satır numaralarında yapılan değişiklikler söz konusu olabilir. Satır içine alma ve özetleme gibi optimizasyonlardan kaynaklanıyor olabilir. En büyük etken, sınıfların ve yöntemlerin bile adları değiştireceği kod karartmadır.

R8, orijinal yığın izlemeyi 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 tekrar izlenebilecek yeterli bilgileri koruduğundan emin olmanız gerekir:

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

LineNumberTable özelliği, konumların yığın izlemelerde yazdırılacağı yöntemlerdeki konum bilgilerini korur. 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ğinden, geri çekme sırasında gerçek orijinal kaynak dosya adı gerekli değildir.

R8 her çalıştırıldığında bir mapping.txt dosyası oluşturur. Bu dosya, yığın izlerini orijinal yığın izleriyle eşleştirmek için gereken bilgileri içerir. Android Studio, dosyayı <module-name>/build/outputs/mapping/<build-type>/ dizinine kaydeder.

Uygulamanızı Google Play'de yayınlarken mapping.txt dosyasını uygulamanızın her sürümü için yükleyebilirsiniz. Android App Bundle kullanarak yayınlarken bu dosya otomatik olarak uygulama paketi içeriğinin bir parçası olarak eklenir. Ardından Google Play, kullanıcı tarafından bildirilen sorunlardan gelen yığın izlemeleri (stack trace) takip ederek bunları Play Console'da inceleyebilirsiniz. Daha fazla bilgi için çökmeyle sonuçlanan yığın izlemelerin (stack trace) kodunu gösterme ile ilgili Yardım Merkezi makalesine bakın.

R8 ile sorun giderme

Bu bölümde, R8 kullanarak daraltma, 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) kod için rapor oluşturma

Belirli R8 sorunlarını gidermenize yardımcı olması için R8'in uygulamanızdan kaldırdığı tüm kodların bir raporunu görmek faydalı olabilir. Bu raporu oluşturmak istediğiniz her modül için özel kurallar dosyanıza -printusage <output-dir>/usage.txt ekleyin. R8'i etkinleştirdiğinizde ve uygulamanızı derlediğinizde, R8 belirttiğiniz yolu ve dosya adını içeren 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 saklama kurallarından belirlediği giriş noktalarının raporunu görmek isterseniz -printseeds <output-dir>/seeds.txt öğesini özel kurallar dosyanıza ekleyin. R8'i etkinleştirdiğinizde ve uygulamanızı derlediğinizde, R8 belirttiğiniz yolu ve dosya adını içeren bir rapor oluşturur. Tutulan 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. (Gradle'dan ayrıntılı metin çıkışını görüntülemek için önce pencerenin sol tarafındaki Görünümü aç/kapat tıklamanız gerekir.) Örnek:

: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 referans verdiği ve hangi kaynakların kullanıldığı veya kaldırıldığı gibi ayrıntıları içerir.

Örneğin, @drawable/ic_plus_anim_016 öğesinin neden hâlâ uygulamanızda olduğunu öğrenmek için resources.txt dosyasını açın ve bu dosya adını arayın. Aşağıdaki gibi başka bir kaynaktan 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 hizmetinin neden ulaşılabilir olduğunu bilmeniz gerekiyor. Daha yukarıya bakarsanız bu kaynağın, "Kök ulaşılabilir 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 (yani R.drawable kimliğinin erişilebilir kodda bulunduğu) anlamına gelir.

Katı denetim kullanmıyorsanız kaynak kimlikleri, dinamik olarak yüklenen kaynaklar için kaynak adları oluşturmakta kullanılabileceği gibi görünen dize sabitleri varsa erişilebilir olarak işaretlenebilir. Bu durumda, derleme çıkışında kaynak adı için arama yaparsanız şuna benzer bir mesaj görebilirsiniz:

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 belirli bir kaynağı dinamik olarak yüklemek için kullanılmadığından eminseniz saklanacak kaynakların özelleştirilmesi ile ilgili bölümde açıklandığı gibi derleme sistemine bu kaynağı kaldırması için tools:discard özelliğini kullanabilirsiniz.