Kitaplık yazarları için optimizasyon

Kitaplık yazarı olarak, uygulama geliştiricilerin kitaplığınızı uygulamalarına kolayca entegre edebilmesini ve yüksek kaliteli bir son kullanıcı deneyimi sunabilmesini sağlamanız gerekir. Bu, kitaplığınızın geliştiricinin ek kurulum yapmasına gerek kalmadan Android optimizasyonuyla (R8) uyumlu olması veya kitaplığın Android'de kullanıma uygun olmayabileceğini belgelemesi gerektiği anlamına gelir. Android'de kullanılmak üzere tasarlanan kitaplıkların, önemli uygulama optimizasyonlarını engellememesi ve ek optimizasyon koşullarına uyması çok önemlidir.

Bu doküman, yayınlanmış kitaplıkların geliştiricilerine yöneliktir ancak büyük ve modüler bir uygulamadaki dahili kitaplık modüllerinin geliştiricileri için de faydalı olabilir.

Uygulama geliştiriciyseniz ve Android uygulamanızı optimize etme hakkında bilgi edinmek istiyorsanız Uygulama optimizasyonunu etkinleştirme başlıklı makaleyi inceleyin. Hangi kitaplıkların kullanıma uygun olduğu hakkında bilgi edinmek için Kitaplıkları akıllıca seçme başlıklı makaleyi inceleyin.

Saklama kuralı türlerini anlama

Kitaplıklarda kullanabileceğiniz iki farklı türde saklama kuralı vardır:

  • Tüketici saklama kuralları, kitaplığın yansıttığı her şeyi saklayan kuralları belirtmelidir. Bir kitaplık, kodunu veya bir istemci uygulaması tarafından tanımlanan kodu çağırmak için yansıtma ya da JNI kullanıyorsa bu kuralların hangi kodun saklanması gerektiğini açıklaması gerekir. Kitaplıklar, uygulama saklama kurallarıyla aynı biçimi kullanan tüketici saklama kurallarını paketlemelidir. Bu kurallar, kitaplık yapılarına (AAR veya JAR) paketlenir ve kitaplık kullanıldığında Android uygulaması optimizasyonu sırasında otomatik olarak tüketilir. Bu kurallar, build.gradle.kts (veya build.gradle) dosyanızda consumerProguardFiles özelliğiyle belirtilen dosyada tutulur. Daha fazla bilgi için Tüketicilerin saklama kurallarını yazma başlıklı makaleyi inceleyin.
  • Kitaplık derleme saklama kuralları, kitaplığınız derlenirken uygulanır. Bu dosyalar yalnızca derleme sırasında kitaplığınızı kısmen optimize etmeye karar verirseniz gereklidir. Kitaplığın genel API'sinin kaldırılmasını önlemeleri gerekir. Aksi takdirde genel API, kitaplık dağıtımında yer almaz. Bu da uygulama geliştiricilerin kitaplığı kullanamayacağı anlamına gelir. Bu kurallar, build.gradle.kts (veya build.gradle) dosyanızda proguardFiles özelliğiyle belirtilen dosyada tutulur. Daha fazla bilgi için AAR kitaplığı derlemesini optimize etme başlıklı makaleyi inceleyin.

Optimizasyon koşulları ve yönergeleri

Kitaplıklardaki R8 yapılandırması, kullanan uygulamanın nihai ikili program boyutu ve performansı üzerinde genel bir etkiye sahiptir. Kitaplık yazarları, genel saklama kuralı en iyi uygulamalarının yanı sıra belirli şartlara uymalı ve ek yönergeleri dikkate almalıdır.

Optimizasyon koşullarına uyma

Kitaplıklardaki verimsizlik; uygulama şişkinliğinin, bellek israfının, yavaş başlatmaların ve ANR'lerin (Uygulama Yanıt Vermiyor hataları) katkıda bulunan en önemli faktörlerindendir. Kitaplıklar, uygulama kalitesini ve kullanıcı deneyimini önemli ölçüde düşürmemek için aşağıdaki şartları ihlal etmekten kaçınmalıdır.

  • Geniş veya paket genelinde saklama kuralları olmamalıdır: Kitaplığınızda, kitaplığınızdaki veya başka bir kitaplıktaki kodun çoğunu saklayan geniş saklama kuralları bulunmamalıdır. Geniş kapsamlı saklama kuralları, kısa vadede kilitlenme sorunlarını çözebilir ancak kitaplığınızı kullanan tüm uygulamaların uygulama boyutunu artırır.

    Kitaplığınızdaki veya referans verilen diğer kitaplıklardaki paketler için paket genelinde saklama kuralları (ör. -keep class com.mylibrary.** {*; }) eklemeyin. Bu tür kurallar, kitaplığınızı kullanan tüm uygulamalarda bu paketlerin optimizasyonunu sınırlar.

  • Uygunsuz genel kurallar yok: -dontobfuscate veya -allowaccessmodification gibi genel seçenekleri asla kullanmayın.

  • Mümkün olduğunda yansıma yerine kod oluşturma kullanma: Mümkün olduğunda yansıma yerine kod oluşturma (codegen) kullanın. Codegen ve yansıtma, programlama sırasında standart koddan kaçınmak için kullanılan yaygın yaklaşımlardır. Ancak codegen, R8 gibi bir uygulama optimizasyon aracıyla daha uyumludur.

    Kod oluşturma ile kod, derleme işlemi sırasında analiz edilir ve değiştirilir. Derleme süresinden sonra önemli bir değişiklik yapılmadığından optimize edici, sonuçta hangi kodun gerekli olduğunu ve hangisinin güvenli bir şekilde kaldırılabileceğini bilir.

    Yansıma sayesinde kod, çalışma zamanında analiz edilir ve değiştirilir. Kod, yürütülene kadar tam olarak kesinleşmediğinden optimize edici, hangi kodun güvenli bir şekilde kaldırılabileceğini bilemez. Bu işlem, çalışma zamanında yansıma yoluyla dinamik olarak kullanılan kodu kaldırabilir ve bu da kullanıcılar için uygulama kilitlenmelerine neden olabilir.

    Birçok modern kitaplık, yansıma yerine kod oluşturma kullanır. Room, Hilt ve diğer birçok kitaplık tarafından kullanılan ortak bir giriş noktası için KSP'ye bakın.

  • R8 tam modunu destekleme: R8 tam modu etkinleştirildiğinde kitaplığınız kilitlenmemelidir. R8'in tam modu, R8'i kullanmak için önerilen moddur ve 2023'te kararlı hale getirilen AGP 8.0'dan beri varsayılan moddur. Kitaplığınız R8 altında kilitleniyorsa çözüm, belirli yansıma veya JNI giriş noktasını belirleyip hedeflenen bir kural eklemektir, tüm paketi saklamak değildir.

Ek öneriler

Optimizasyon koşullarının yanı sıra aşağıdaki ek önerileri de dikkate alın.

  • Kitaplığınızın tüketici saklama kuralları dosyasında -repackageclasses kullanmayın. Ancak kitaplık derlemenizi optimize etmek için kitaplığınızın derleme tutma kuralları dosyasında -repackageclasses ile <your.library.package>.internal gibi dahili bir paket adı kullanabilirsiniz. Bu, kitaplığınızın optimize edilmemiş uygulamalardaki verimliliğini artırabilir. Ancak uygulamaların da optimize edilmesi gerektiğinden genellikle buna gerek yoktur.
  • Kitaplığınızın çalışması için gereken tüm özellikleri, proguard-android-optimize.txt içinde tanımlanan özelliklerle çakışma olsa bile kitaplığınızın saklama kuralları dosyalarında beyan edin.
  • Kitaplığınızın dağıtımında aşağıdaki özelliklerin bulunmasını zorunlu kılıyorsanız bunları kitaplığınızın derleme saklama kuralları dosyasında tutun ve kitaplığınızın tüketici saklama kuralları dosyasında tutmayın:
    • AnnotationDefault
    • EnclosingMethod
    • Exceptions
    • InnerClasses
    • RuntimeInvisibleAnnotations
    • RuntimeInvisibleParameterAnnotations
    • RuntimeInvisibleTypeAnnotations
    • RuntimeVisibleAnnotations
    • RuntimeVisibleParameterAnnotations
    • RuntimeVisibleTypeAnnotations
    • Signature
  • Kitaplık yazarları, çalışma zamanında ek açıklamalar kullanılıyorsa tüketici saklama kurallarında RuntimeVisibleAnnotations özelliğini tutmalıdır.
  • Kitaplık yazarları, tüketici saklama kurallarında aşağıdaki genel seçenekleri kullanmamalıdır:
    • -include
    • -basedirectory
    • -injars
    • -outjars
    • -libraryjars
    • -repackageclasses
    • -flattenpackagehierarchy
    • -allowaccessmodification
    • -renamesourcefileattribute
    • -ignorewarnings
    • -addconfigurationdebugging
    • -printconfiguration
    • -printmapping
    • -printusage
    • -printseeds
    • -applymapping
    • -obfuscationdictionary
    • -classobfuscationdictionary
    • -packageobfuscationdictionary

Yansıtmanın uygun olduğu durumlar

Yansıma kullanmanız gerekiyorsa yalnızca aşağıdakilerden birine yansıtmanız gerekir:

  • Belirli hedeflenen türler (belirli arayüz uygulayıcıları veya alt sınıflar)
  • Belirli bir çalışma zamanı ek açıklamasını kullanan kod

Yansımayı bu şekilde kullanmak, çalışma zamanı maliyetini sınırlandırır ve hedeflenen tüketici saklama kuralları yazmayı sağlar.

Bu özel ve hedeflenmiş yansıtma biçimi, hem Android çerçevesinde (ör. etkinlikler, görünümler ve çizilebilir öğeler şişirilirken) hem de AndroidX kitaplıklarında (ör. WorkManager ListenableWorkers veya RoomDatabases oluşturulurken) görebileceğiniz bir kalıptır. Buna karşılık, Gson'un açık uçlu yansıtması Android uygulamalarında kullanıma uygun değildir.

Yaygın yanlış kanılar

Sık karşılaşılan birkaç yanlış bilgi, R8'i yanlış yapılandırmanıza neden olabilir. Bu politikalardan bazıları:

  • R8'in optimizasyonları hakkında yanlış bilgi: Yaygın kanının aksine, R8'in optimizasyonları yalnızca kod karartma ile sınırlı değildir. Aynı zamanda yöntem içi yerleştirme ve sınıf birleştirme teknikleriyle kod küçültme ve mantıksal optimizasyonları da içerir. Daha fazla bilgi için R8 optimizasyonuna genel bakış başlıklı makaleyi inceleyin.

  • Karartılmış kitaplıkların optimizasyonunu atlama: Sık yapılan bir hata, bir kitaplığın AAR (Android Arşivi) veya JAR (Java Arşivi) olarak derlenirken optimize edilmesi ya da karartılması nedeniyle optimizasyonun dışında bırakılmasıdır. Kitaplık derleme süresi sırasındaki optimizasyonlar sınırlıdır ve uygulamanız, kitaplığı bir saklama kuralına dahil ederek kitaplığın optimizasyonunu devre dışı bırakmamalıdır. Daha fazla bilgi için AAR kitaplığı derlemesini optimize etme başlıklı makaleyi inceleyin.

  • -keep seçeneğinin yanlış anlaşılması -keep kuralı, R8'in optimizasyon adımlarından herhangi birini çalıştırmasını engeller. Daha fazla bilgi için Doğru saklama seçeneğini belirleme başlıklı makaleyi inceleyin.

Tüketici saklama kuralı kalitesini doğrulama

Kitaplığınızın bir uygulamaya yerleştirildiğinde çok fazla kod optimizasyonunu engellemediğini doğrulamak için kitaplık örneği veya entegrasyon testi uygulaması kullanın. R8'i etkinleştirin ve R8 Yapılandırma Analiz Aracı ile uygulamanın genel R8 yapılandırma kalitesini inceleyin.

Tüketici kurallarınız, kitaplığınızın kodunun büyük bir bölümünde optimizasyonu sınırlayacak şekilde gösteriliyorsa bu, tüketici kurallarınızın iyileştirilmesi gerektiği anlamına gelir. Tüketici kurallarını daraltın ve kitaplığınızın etkilenen tüm yönlerini R8'in etkin olduğu bir uygulamada test edin.

Kural paketlemeyi yapılandırma

Tüketici saklama kurallarınızın doğru şekilde uygulandığından emin olmak için bunları kitaplık biçiminize göre uygun şekilde paketlemeniz gerekir.

Kitaplığınızın yansıma ve JNI kullanımını bildirmek için Jetpack Keep'i kullanırsanız Gradle eklentisi, oluşturulan kuralları sizin için tüketici saklama kuralları olarak paketleyebilir.

AAR kitaplıkları

AAR kitaplığı için tüketici kuralları eklemek istiyorsanız Android kitaplık modülünün derleme komut dosyasında consumerProguardFiles seçeneğini kullanın. Daha fazla bilgi için kitaplık modülleri oluşturmayla ilgili kılavuzumuzu inceleyin.

Kotlin

android {
    defaultConfig {
        consumerProguardFiles("consumer-proguard-rules.pro")
    }
    ...
}

Modern

android {
    defaultConfig {
        consumerProguardFiles 'consumer-proguard-rules.pro'
    }
    ...
}

JAR kitaplıkları

Kuralları, JAR olarak gönderilen Kotlin veya Java kitaplığınızla birlikte paketlemek için kurallar dosyanızı nihai JAR'ın META-INF/proguard/ dizinine herhangi bir dosya adıyla yerleştirin. Örneğin, kodunuz <libraryroot>/src/main/kotlin ise <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro konumuna bir tüketici kuralları dosyası yerleştirin. Kurallar, çıkış JAR'ınızda doğru konuma yerleştirilir.

Kuralların META-INF/proguard dizininde olduğunu kontrol ederek nihai JAR'ın kuralları doğru şekilde paketlediğini doğrulayın.

AAR kitaplığı derlemesini optimize etme (ileri düzey)

Kitaplık derleme süresinde olası optimizasyonlar çok sınırlı olduğundan, genellikle kitaplık derlemesini doğrudan optimize etmeniz gerekmez. Kitaplık geliştiricisi olarak, kitaplığı optimize etmeden önce optimizasyonun birden fazla aşamasını göz önünde bulundurmanız ve kitaplık ile uygulama derleme zamanında davranışları korumanız gerekir.

Kitaplığınızı derleme sırasında optimize etmek istiyorsanız bu işlem Android Gradle eklentisi tarafından desteklenir.

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        configureEach {
            consumerProguardFiles("consumer-rules.pro")
        }
    }
}

Modern

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                'proguard-rules.pro'
        }
        configureEach {
            consumerProguardFiles "consumer-rules.pro"
        }
    }
}

proguardFiles davranışının consumerProguardFiles davranışından çok farklı olduğunu unutmayın:

  • proguardFiles, derleme sırasında genellikle getDefaultProguardFile("proguard-android-optimize.txt") ile birlikte kullanılarak kitaplık derlemesi sırasında kitaplığınızın hangi bölümünün saklanması gerektiğini tanımlar. En azından, bu sizin genel API'nizdir.
  • consumerProguardFiles ise kitaplığınızın derlenmesi sırasında daha sonra hangi optimizasyonların yapılacağını etkilemek için kitaplığa paketlenir.

Örneğin, kitaplığınız dahili sınıflar oluşturmak için yansıma kullanıyorsa saklama kurallarını hem proguardFiles hem de consumerProguardFiles içinde tanımlamanız gerekebilir.

Kitaplığınızın derlemesinde -repackageclasses kullanıyorsanız sınıfları kitaplığınızın paketinin içindeki bir alt pakete yeniden paketleyin. Örneğin, -repackageclasses 'internal' yerine -repackageclasses 'com.example.mylibrary.internal' kullanın.

Farklı R8 sürümlerini destekleme (gelişmiş)

Kuralları, R8'in belirli sürümlerini hedefleyecek şekilde özelleştirebilirsiniz. Bu sayede kitaplığınız, daha yeni R8 sürümlerini kullanan projelerde en iyi şekilde çalışırken mevcut kurallar daha eski R8 sürümlerini kullanan projelerde kullanılmaya devam edebilir.

Hedeflenen R8 kurallarını belirtmek için bunları bir AAR'nin classes.jar içindeki META-INF/com.android.tools dizinine veya bir JAR'ın META-INF/com.android.tools dizinine eklemeniz gerekir.

In an AAR library:
    proguard.txt (legacy location, the file name must be "proguard.txt")
    classes.jar
    └── META-INF
        └── com.android.tools (location of targeted R8 rules)
            ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
            └── ... (more directories with the same name format)

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rule-files> (legacy location)
    └── com.android.tools (location of targeted R8 rules)
        ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
        └── ... (more directories with the same name format)

META-INF/com.android.tools dizininde, kuralların hangi R8 sürümleri için yazıldığını belirtmek amacıyla r8-from-<X>-upto-<Y> biçiminde adlara sahip birden fazla alt dizin olabilir. Her alt dizinde, R8 kurallarını içeren bir veya daha fazla dosya bulunabilir. Bu dosyalar herhangi bir dosya adına ve uzantısına sahip olabilir.

-from-<X> ve -upto-<Y> bölümlerinin isteğe bağlı olduğunu, <Y> sürümünün özel olduğunu ve sürüm aralıklarının genellikle kesintisiz olduğunu ancak çakışabileceğini unutmayın.

Örneğin, r8, r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0 ve r8-from-8.2.0, bir dizi hedeflenen R8 kuralını temsil eden dizin adlarıdır. r8 dizinindeki kurallar tüm R8 sürümleri tarafından kullanılabilir. r8-from-8.0.0-upto-8.2.0 dizinindeki kurallar, 8.0.0 sürümünden 8.2.0 sürümüne kadar (8.2.0 hariç) R8 tarafından kullanılabilir.

Android Gradle eklentisi, mevcut R8 sürümü tarafından kullanılabilecek tüm kuralları seçmek için bu bilgileri kullanır. Bir kitaplık, hedeflenen R8 kurallarını belirtmezse Android Gradle eklentisi, kuralları eski konumlardan (AAR için proguard.txt veya JAR için META-INF/proguard/<ProGuard-rule-files>) seçer.