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

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ştirirken uygulamanızın sınıf ve üyelerinin adlarını kısaltan karartma özelliğinden 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 derleme zamanındaki bu görevleri projeniz için 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ü kullanarak oluşturduğunuzda, eklenti artık derleme zamanı kod optimizasyonu gerçekleştirmek için ProGuard'ı kullanmaz. Eklenti bunun yerine, aşağıdaki derleme zamanı görevlerini işlemek için R8 derleyicisiyle birlikte çalışır:

  • Kod küçültme (veya ağaç sallama): Kullanılmayan sınıfları, alanları, yöntemleri ve özellikleri uygulamanızdan ve kitaplık bağımlılıklarını tespit edip güvenli bir şekilde kaldırarak 64.000 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ırsanız küçültme işlemi, 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 daraltma 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ığında artık referans verilmeyen tüm 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ısaltarak 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 ifadesinin hiçbir zaman else {} kolunun alınmadığını tespit ederse else {} dalının kodunu kaldırır. Daha fazla bilgi edinmek için kod optimizasyonu bölümüne gidin.

Uygulamanızın sürüm sürümünü oluştururken R8, yukarıda sizin için açıklanan derleme süresi görevlerini 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'i kullanacak şekilde güncellemek, mevcut kurallarınızı değiştirmenizi gerektirmemelidir.

Daraltmayı, 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 tutacağı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. Daraltma, kod karartma ve optimizasyonu etkinleştirmek için aşağıdaki kodu proje düzeyindeki derleme komut dosyanıza 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 koduna giriş noktası görevi gören sınıflar gibi uygulamanızın yapısını 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 bir proguard-rules.pro dosyası oluşturur.

Varsayılan olarak, bu dosya herhangi bir kural için geçerli değildir. 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şturulur. Android Gradle eklentisi, çoğu Android projesi için yararlı olan kurallar içeren ve @Keep* ek açıklamalarını etkinleştiren proguard-android-optimize.txt özelliğini oluşturur.

Varsayılan olarak, Android Studio'yu kullanarak yeni bir modül oluştururken modül düzeyindeki derleme komut dosyası, bu kurallar dosyasını sizin için sürüm derlemenize ekler.

Not: Android Gradle eklentisi, önceden tanımlanmış ilave 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/

Kendi ProGuard kural dosyasıyla yayınlanan AAR kitaplığını derleme zamanı bağımlılığı olarak eklerseniz R8, projenizi derlerken kendi kurallarını otomatik olarak uygular.

Kitaplığın düzgün çalışması için belirli saklama kuralları gerekiyorsa (yani kitaplık geliştiricisi sizin için sorun giderme adımlarını gerçekleştirdiyse), AAR kitaplıklarıyla paketlenmiş kural dosyalarını kullanmak faydalı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 derlemesini etkileyebileceğini unutmayın. Örneğin, bir kitaplıkta kod optimizasyonlarını devre dışı bırakan bir kural bulunuyorsa bu kural projenizin tamamında 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, düzenlerdeki ve diğer uygulama kaynaklarındaki sınıflara yapılan referanslara dayanarak 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. 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 ilgili sorunları giderirken bu bilgiyi aklınızda bulundurun.

Projenizi oluştururken R8'in uyguladığı tüm kuralların tam 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. Ayrıca, diğer dosyalardan ek kurallar eklemek için bunları modülünüzün derleme komut dosyasındaki proguardFiles özelliğine ekleyebilirsiniz.

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

Ayrıca, yalnızca test APK'sında bulunan 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")
        }
    }
}

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'
            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üçü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çeriyor 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 küçültmek için ilk olarak R8, birleştirilmiş yapılandırma dosyaları grubunu temel alarak 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 bir grafiğini oluşturmak için uygulamanızın kodunu inceler. Bu grafiğe bağlı olmayan koda erişilemez 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ına dayalı bir grafik oluşturur.

R8, giriş noktalarını projenin R8 yapılandırma dosyalarındaki -keep kuralları aracılığıyla belirler. Yani Keep kuralları, R8'in uygulamanızı daraltırken 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 sizin için gerektirdiği saklama kurallarını (ör. uygulamanızın etkinlikleri, görünümleri ve hizmetleri) otomatik olarak oluşturur. Ancak bu varsayılan davranışı ek saklama kurallarıyla özelleştirmeniz gerekirse hangi kodun korunacağı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.

Hangi kodun saklanacağını özelleştirin

Çoğu durumda, varsayılan ProGuard kural 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 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. yansımayla)

Uygulamanızı test etmek, uygunsuz bir şekilde kaldırılan koddan kaynaklanan tüm hataları ortaya çıkarmalıdır ancak kaldırılan kodla ilgili bir rapor oluşturarak kaldırılan kodu da inceleyebilirsiniz.

Hataları düzeltmek ve R8'i belirli kodları kullanmaya 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 eklemek tüm sınıfı olduğu gibi korur. Bir yönteme veya alana eklediğinizde, sınıf adının yanı sıra yöntem/alan (ve adı) da aynı kalır. Bu ek açıklamanın yalnızca AndroidX Ek Açıklama Kitaplığı kullanılırken ve Android Gradle eklentisiyle paketlenmiş olan ProGuard kuralları dosyası eklediğinizde küçültmeyi etkinleştirme ile ilgili bölümde açıklandığı gibi kullanıldığını unutmayın.

-keep seçeneğini kullanırken dikkat etmeniz 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 çıkarı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 çıkarma işlemi, simge tablosunu ve uygulamanızın kullandığı yerel kitaplıklarda bulunan hata ayıklama bilgilerinin kaldırılmasından oluşur. 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 kilitlenmeleri teşhis etmek imkansızdır.

Yerel kilitlenme desteği

Google Play Console, yerel kilitlenmeleri Android vitals altında bildirir. Uygulamanız için yerel hata ayıklama simgeleri dosyasını birkaç adımda 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 çıktısına göre 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ğı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 kodunu kullanın. Bu seviye 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 kodunu 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ı manuel olarak Google Play Console'a 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 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
  1. Bu dizinin içeriğini zip dosyası olarak kaydedin:

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

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

Kaynak daraltma, yalnızca kod daraltma özelliğiyle birlikte çalışır. Kod küçültücü kullanılmayan tüm kodları kaldırdıktan sonra kaynak küçültücü, uygulamanın hangi kaynakları kullanmaya devam ettiğini belirleyebilir. Bu durum özellikle kaynak içeren kod kitaplıkları eklediğinizde geçerlidir. Kitaplık kaynaklarının referans alınmaması ve dolayısıyla kaynak kırıcı tarafından kaldırılabilir olması 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 küçültme için minifyEnabled kullanarak uygulamanızı henüz oluşturmadıysanız shrinkResources özelliğini 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 korumak için proguard-rules.pro dosyanızı düzenlemeniz gerekebilir.

Hangi kaynakların tutulacağını özelleştirin

Saklamak veya silmek istediğiniz belirli kaynaklar varsa projenizde <resources> etiketine sahip bir XML dosyası oluşturun. Ardından, 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 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 konumunda) kaydedin. Derleme, bu dosyayı uygulamanıza paketlemez.

Hangi kaynakların silineceğini belirtmek, bunları silmek yerine silmek saçma görünebilir ancak derleme varyantlarını kullanırken yararlı olabilir. Örneğin, tüm kaynaklarınızı ortak proje dizinine yerleştirip belirli bir kaynağın kodda kullanıldığını (dolayısıyla küçültücü tarafından kaldırılmadığını) ve 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 daha sonra kaynak analizcisinin, gerçekten başvuruda bulunulan bir kaynak ile kodda aynı değere sahip olan tam sayı değeri arasındaki farkı bilmemesi olabilir.

Sıkı referans kontrollerini etkinleştirin

Normalde kaynak küçültücü, bir kaynağın kullanılıp kullanılmadığını doğru bir şekilde belirleyebilir. Ancak kodunuz Resources.getIdentifier() öğesine (veya kitaplıklarınızdan herhangi biri bunu AppCompat kitaplığına çağrı yapıyorsa) bu, kodunuzun dinamik olarak oluşturulan dizelere dayalı olarak kaynak adlarını aradığı anlamına gelir. 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ılmaya uygun değil olarak işaretler.

Örneğin, aşağıdaki kod img_ önekine 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'a benzer bir biçimdeki kaynak URL'lerini arar. Araç, bunun gibi URL'ler oluşturmak için kullanılabilecek bu gibi dizeler bulursa bunları kaldırmaz.

Bunlar, varsayılan olarak etkin olan güvenli daraltma modu örnekleridir. Ancak, bu "pişman olmaktan daha güvenli" işlemeyi kapatabilir ve kaynak küçültücünün yalnızca kullanıldığından emin olduğu kaynakları tuttuğunu belirtebilirsiniz. Bunun için keep.xml dosyasında aşağıdaki gibi 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 referansta bulunursa tools:keep özelliğini kullanarak bu kaynakları manuel olarak tutmanız gerekir.

Kullanılmayan alternatif kaynakları kaldırın

Gradle kaynak küçültücü, yalnızca uygulama kodunuzun başvuruda bulunmadığı kaynakları kaldırır. Bu da farklı cihaz yapılandırmaları için alternatif kaynakları kaldırmayacağı anlamına gelir. 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, bu kitaplıklardaki mesajlar için çevrilmiş tüm dil dizelerini içerir. Bu durumda uygulamanızın geri kalanı aynı dillere çevrilsin veya çevrilmesin. Yalnızca uygulamanızın resmi olarak desteklediği dilleri korumak istiyorsanız resConfig özelliğini kullanarak bu dilleri belirtebilirsiniz. Belirtilmeyen dillerin kaynakları kaldırılır.

Aşağıdaki snippet'te dil kaynaklarınızı yalnızca İngilizce ve Fransızca olacak şekilde 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, uygulama yüklenirken varsayılan olarak yalnızca kullanıcının cihazında yapılandırılmış 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 Uygulama Paketi yapılandırmasına bakın.

APK'larla (Ağustos 2021'den önce oluşturulan) yayınlanan eski uygulamalarda, 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 olabilecek aynı ada sahip çekmeceler 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ü, 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 niteleyicisini 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 nihai yapıda dağıtılmak üzere yalnızca bu kaynağı AAPT'ye geçirir.

Gradle aşağıdaki konumlarda yinelenen kaynaklar arar:

  • Ana kaynak grubuyla ilişkili olan ve genellikle src/main/res/ konumunda bulunan ana kaynaklar.
  • Derleme türü ve derleme çeşitlerinden 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 yapı türü bölümünde yinelenen bir kaynak görünüyorsa Gradle, derleme türünden olanı seçer.

Aynı kaynak kümesinde benzer kaynaklar görünüyorsa 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ızın sınıflarının, yöntemlerinin ve alanlarını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 işlemi, kodu uygulamanızdan kaldırmaz ancak çok sayıda sınıf, yöntem ve alanı dizine ekleyen DEX dosyaları olan uygulamalarda önemli ölçüde boyut tasarrufu görülebilir. 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 (stacktrace) 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öntemleri ve sınıfları için tahmin edilebilir adlandırmaya dayanıyorsa örneğin yansıtma kullanırken bu imzaları giriş noktası olarak değerlendirmeniz ve bunlar için saklama kuralları belirlemeniz gerekir. Hangi kodun saklanacağını özelleştirme konulu bölümde açıklandığı gibi, bu imzalar için saklama kuralları belirlemeniz gerekir. Bu Keep kuralları, R8'e bu kodu uygulamanızın son DEX'inde tutmasının yanı sıra orijinal adını da korumasını söyler.

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ızsa). 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 veya mümkün olduğunda kodunuzu daha az ayrıntılı olacak şekilde yeniden yazmak için kodunuzu daha derin bir düzeyde 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 {} şubesinin kodunu kaldırabilir.
  • Kodunuz yalnızca bir yerdeki bir yöntemi çağırırsa R8, yöntemi kaldırabilir ve tek çağrı sitesinde satır içine alabilir.
  • R8, bir sınıfın yalnızca bir benzersiz alt sınıfı olduğunu belirlerse ve sınıfın kendisinin soyutlanmadığını (örneğin, yalnızca bir beton uygulama sınıfı tarafından kullanılan soyut bir temel sınıf) belirlerse 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, tekli optimizasyonları devre dışı bırakmanıza veya etkinleştirmenize ya da bir optimizasyonun davranışını değiştirmenize izin vermez. Aslında R8, varsayılan optimizasyonları değiştirmeye çalışan -optimizations ve - optimizationpasses gibi ProGuard kurallarını yok sayar. Bu kısıtlama önemlidir, çünkü R8 gelişmeye devam ettikçe, optimizasyonlar için standart bir davranışın korunması, 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) nasıl edineceğinizi öğrenmek için geri çekme ile ilgili bölüme 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 itibaren 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 Reflection API üzerinden bir sınıfa başvurduğunu varsayalım. "Tam modu" kullanmadığınızda R8, çalışma zamanında ilgili sınıfın nesnelerini incelemek ve değiştirmek istediğinizi (kodunuz gerçekten yapmasa bile) varsayar ve sınıfı ve statik başlatıcısını otomatik olarak korur.

Bununla birlikte, "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 sınıfı uygulamanızın son DEX'inden kaldırır. Yani sınıfı ve statik başlatıcısını saklamak 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 izlemeleri geri çekme

Yığın izlemeleri kaynak koduna tam olarak karşılık gelmeyeceğinden, R8 tarafından işlenen kod, yığın izlemelerin anlaşılmasını zorlaştırabilecek çeşitli yollarla değiştirilir. Bu durum, hata ayıklama bilgileri saklanmadığında satır numaralarındaki değişiklikler için geçerli olabilir. Bunun nedeni, satır içine alma ve anahatlama gibi optimizasyonlar olabilir. En büyük katkısı, sınıf ve yöntemlerin bile adları değiştirdiği 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 izlerinin yeniden taranmasını desteklemek için, modülünüzün proguard-rules.pro dosyasına aşağıdaki kuralları ekleyerek derlemenin yeniden izlenecek yeterli bilgiyi sakladığından emin olmanız gerekir:

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

LineNumberTable özelliği, bu konumların yığın izlemelere yazdırılacağı yöntemlerdeki konum bilgilerini tutar. SourceFile özelliği, tüm potansiyel çalışma zamanlarının gerçekte konum bilgilerini 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 yeniden inceleme yaparken gerçek orijinal kaynak dosya adına gerek yoktur.

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

Uygulamanızı Google Play'de yayınladığınızda, uygulamanızın her sürümü için mapping.txt dosyasını yükleyebilirsiniz. Android App Bundle'ı kullanarak yayınladığınızda bu dosya, otomatik olarak uygulama paketi içeriğinin bir parçası olarak eklenir. Ardından Google Play, kullanıcılar 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ı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 koruma kurallarından belirlediği giriş noktalarının raporunu görmek isterseniz özel kurallar dosyanıza -printseeds <output-dir>/seeds.txt ifadesini ekleyin. R8'i etkinleştirip uygulamanızı derlediğinizde R8, belirttiğiniz yol ve dosya adını içeren bir rapor oluşturur. Saklanan giriş noktaları 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, Derle penceresinde uygulamadan kaldırılan kaynakların bir özeti gösterilir. (Gradle'dan alınan 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çın ve 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] &#64;drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     &#64;drawable/ic_plus_anim_016

Şimdi @drawable/add_schedule_fab_icon_anim sitesine neden erişilebildiğini bilmeniz gerekir. Yukarı doğru arama yaptığınızda, "Ulaşılabilir kök kaynaklar:" başlığı altında bu kaynağın listelendiğini görürsünüz. Bu, add_schedule_fab_icon_anim için bir kod referansı bulunduğu (yani R.drawable kimliği erişilebilir kodda bulunduğu) anlamına gelir.

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 derleme sistemini kaldırması için bilgilendirmek üzere saklanacak kaynakları özelleştirme bölümünde açıklandığı gibi tools:discard özelliğini kullanabilirsiniz.