İçeriğe Atla
Mustafa Erbay
Teknoloji · 8 dk okuma · görüntülenme Read in English

Mobil Uygulama Boyutu Optimizasyonu: Geliştirme Sürecinin Yükü

Mobil uygulama boyutu optimizasyonu sürecinde APK ve IPA paketlerini küçültme yöntemlerini, R8/ProGuard ayarlarını ve CI/CD süreçlerini inceliyoruz.

100%

Mobil uygulama boyutu optimizasyonu, markete ilk yükleme anından itibaren dönüşüm (conversion) oranlarını doğrudan etkileyen, ancak geliştirme süreçlerinde genellikle en son aşamaya bırakılan teknik bir borçtur. Birçok projede “çalışsın da boyutu sonra hallederiz” mantığıyla hareket edildiğini, ardından yüzlerce megabayta ulaşmış hantal paketlerle karşılaşıldığını bizzat gördüm. Kendi geliştirdiğim Android spam engelleme uygulamasında ilk derlemede onlarca megabayt olan APK boyutunu, kurduğum optimize edilmiş üretim hattıyla küçük bir kesrine düşürmeyi başardım. Bu süreçte karşılaştığım “build” patlamalarını ve uyguladığım pratik yöntemleri bu rehberde adım adım paylaşıyorum.

Google Play Store verilerine göre, 100 MB üzerindeki her 6 MB’lık artış, yükleme dönüşüm oranını yaklaşık %1 oranında düşürüyor. Kullanıcıların hücresel veri limitleri ve cihazlarındaki depolama baskısı düşünüldüğünde, paket boyutunu minimize etmek teknik bir lüks değil, doğrudan iş başarısını etkileyen bir zorunluluktur. Bu yazıda, modern mobil platformlarda paketlerin anatomisini çıkararak gereksiz yüklerden nasıl kurtulacağımızı inceleyeceğiz.


APK ve IPA Anatomisi: Dosya Boyutunu Şişiren Unsurlar

Bir optimizasyon işlemine başlamadan önce, düşmanımızı tanımamız gerekir. Derlenmiş bir Android (APK) veya iOS (IPA) paketi, aslında özel olarak sıkıştırılmış birer ZIP arşivinden ibarettir. Bu arşivi açtığınızda karşınıza çıkan dosyaların her biri, uygulamanın çalışması için gereken farklı bileşenleri temsil eder.

Aşağıdaki tabloda, optimize edilmemiş standart bir hibrit/native mobil uygulamanın paket içeriğinin tipik dağılımını ve hedeflememiz gereken optimize edilmiş oranları görebilirsiniz:

Bileşen Adı Görevi Optimize Edilmemiş Oran Hedeflenen Oran Uygulanacak Çözüm
classes.dex / Mach-O Derlenmiş kaynak kodlar %25 - %35 %10 - %15 ProGuard, R8, Tree Shaking
res / Assets Görseller, ikonlar, fontlar %40 - %50 %15 - %20 WebP dönüşümü, SVG, Lottie
lib (Native .so files) C/C++ kütüphaneleri, NDK %20 - %30 %5 - %10 ABI Split, App Bundle (AAB)
resources.arsc XML kaynak haritası %5 - %10 %2 - %3 String ve ID optimizasyonu

Gördüğünüz gibi, paket boyutunun neredeyse yarısını görsel kaynaklar (assets) ve native kütüphaneler oluşturuyor. Eğer uygulamanızda harici bir C++ kütüphanesi (örneğin SQLCipher veya özel bir görüntü işleme motoru) kullanıyorsanız, derleyicinin her işlemci mimarisi için (arm64-v8a, armeabi-v7a, x86_64) ayrı bir .so dosyası ürettiğini fark etmişsinizdir. Bu mimarilerin tamamını tek bir pakete koyup göndermek, kullanıcının cihazına hiç kullanmayacağı işlemci kodlarını indirtmek anlamına gelir.

Daha önce ele aldığım Docker imaj boyutu azaltma yöntemleri yazısında olduğu gibi, mobil tarafta da katmanları analiz etmek ve gereksiz bağımlılıkları ayıklamak benzer bir felsefeye dayanıyor. Şimdi bu bileşenleri tek tek nasıl eriteceğimizi pratik kod örnekleriyle ele alalım.


R8 ve ProGuard ile Kod Seviyesinde Daraltma (Shrinking)

Android ekosisteminde kod daraltma, kullanılmayan sınıfların, metotların ve niteliklerin tespit edilip silinmesi işlemidir. Google, eski ProGuard yerine artık varsayılan olarak R8 derleyicisini kullanıyor. R8 sadece kullanılmayan kodları silmekle kalmaz, aynı zamanda sınıfları optimize eder, isimlerini kısaltır (obfuscation) ve kodun tersine mühendislikle okunmasını zorlaştırır.

R8’i aktif etmek için android/app/build.gradle dosyanızda aşağıdaki yapılandırmayı uygulamamız gerekiyor. Bu ayarı açmak, classes.dex boyutunda genellikle kayda değer bir düşüş sağlar:

android {
    compileSdkVersion 34

    buildTypes {
        release {
            // Kod daraltmayı aktif et
            minifyEnabled true
            
            // Kullanılmayan kaynakları (res) temizle
            shrinkResources true
            
            // R8 kurallarını içeren dosyaları tanımla
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

Ancak minifyEnabled true yapmak, çoğu zaman uygulamanın ilk derlemede patlamasına yol açar. Bunun sebebi, uygulamanızda veya kullandığınız üçüncü parti kütüphanelerde bulunan “Reflection” (yansıma) mekanizmalarıdır. Örneğin, bir JSON verisini Java/Kotlin nesnesine dönüştüren Gson veya Moshi gibi kütüphaneler, çalışma zamanında (runtime) sınıf isimlerini ararlar. R8 bu sınıfların isimlerini a, b, c şeklinde kısalttığı için kütüphane sınıfı bulamaz ve NullPointerException fırlatır.

Bu durumu çözmek için proguard-rules.pro dosyasına ilgili kütüphanelerin sınıflarını koruyacak kuralları (Keep Rules) eklemeliyiz. İşte production ortamında kullandığım kararlı bir temel kural seti:

# Gson kütüphanesi için reflection koruması
-keepattributes Signature, *Annotation*, EnclosingMethod, InnerClasses
-keep class com.google.gson.reflect.TypeToken { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Kendi veri modellerinizi (Data Transfer Objects) koruyun
-keep class com.mustafaerbay.app.models.** { *; }

# Log satırlarını production build'inden tamamen temizleyin
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int d(...);
}

Asset Yönetimi: WebP Dönüşümü ve Vektörel Çözümler

Mobil uygulama boyutunu şişiren en büyük günahkar her zaman görsellerdir. Tasarımcıların Figma’dan export ettiği 3x PNG dosyaları doğrudan projeye eklendiğinde, sadece bir açılış ekranı (splash screen) bile pakete 5-10 MB ek yük getirebilir.

İlk kuralımız net: Asla PNG veya JPEG formatında statik görsel kullanmayın. Tüm raster görselleri modern WebP formatına dönüştürmeliyiz. WebP, PNG’ye kıyasla kaliteden ödün vermeden ortalama %30-50 oranında daha iyi sıkıştırma sağlar. Terminal üzerinden tüm proje klasöründeki PNG dosyalarını otomatik olarak WebP’ye dönüştürmek için yazdığım şu shell betiğini CI/CD süreçlerinizde kullanabilirsiniz:

#!/bin/bash
# png_to_webp.sh
# Proje içindeki tüm PNG dosyalarını tarar ve %85 kalitede WebP'ye dönüştürür.

find ./app/src/main/res/ -name "*.png" | while read -r png_file; do
    webp_file="${png_file%.png}.webp"
    # cwebp aracını kullanarak dönüşümü yap
    cwebp -q 85 "$png_file" -o "$webp_file"
    
    if [ -f "$webp_file" ]; then
        echo "Dönüştürüldü: $png_file -> $webp_file"
        rm "$png_file" # Orijinal PNG dosyasını sil
    fi
done

İkinci adım ise ikonlar için kesinlikle VectorDrawable (Android) veya PDF/SVG (iOS) kullanmaktır. Vektörel dosyalar sadece birkaç kilobayt boyutundadır ve her ekran çözünürlüğünde piksellenmeden kusursuz görünürler. Eğer uygulamanızda karmaşık animasyonlar varsa, bunları video veya GIF olarak gömmek yerine Lottie (JSON tabanlı animasyon kütüphanesi) kullanarak vektörel tabanlı animasyonlara dönüştürün. Bu sayede megabaytlarca yer kaplayan bir tanıtım videosunu, boyutu çok daha küçük bir JSON dosyasıyla ikame edebilirsiniz.


Native Kütüphaneler ve ABI Split Yapılandırması

Modern mobil uygulamalar sıklıkla native C/C++ kütüphanelerine ihtiyaç duyar. Örneğin, SQLite veritabanı şifrelemesi, imaj işleme kütüphaneleri veya performans gerektiren kriptografik işlemler native derlenmiş .so (Shared Object) dosyaları gerektirir.

Varsayılan yapılandırmada, derleyici tüm hedef mimariler için bu binary dosyalarını tek bir APK içine gömer (Universal APK). Ancak bir kullanıcının cihazı ya arm64-v8a mimarisidir ya da nadiren armeabi-v7a’dır; ikisine birden asla sahip olamaz. Bu gereksiz yükü önlemek için build.gradle dosyasında ndk.abiFilters yapılandırmasını kurmalı veya markete yükleme yaparken Android App Bundle (AAB) formatını tercih etmeliyiz.

Eğer uygulamanızı doğrudan APK olarak dağıtmanız gereken bir senaryo varsa (örneğin kurumsal içi dağıtım veya alternatif uygulama mağazaları), Gradle üzerinde “ABI Split” özelliğini aktif ederek her mimari için ayrı APK üretebilirsiniz:

android {
    splits {
        // ABI seviyesinde split yapılandırması
        abi {
            enable true
            reset()
            include "armeabi-v7a", "arm64-v8a"
            universalApk false // Tüm mimarileri içeren tek bir universal APK üretme
        }
    }
}

Bu yapılandırma derleme süresini biraz uzatır çünkü Gradle arka arkaya birden fazla APK derler. Ancak günün sonunda kullanıcıya inecek olan APK boyutu neredeyse yarı yarıya düşer. Google Play Store için derleme alıyorsanız, ABI split yerine doğrudan .aab formatında çıktı almanız yeterlidir; Google Play, kullanıcının cihaz mimarisine göre dinamik olarak en uygun APK’yı üretip servis eder.


Dynamic Feature Delivery ile İsteğe Bağlı Yükleme

Uygulamanızın her özelliği, her kullanıcı için her an gerekli olmayabilir. Örneğin, bir e-ticaret uygulamasında “Fatura Görüntüleme/PDF Oluşturma” modülü veya “Müşteri Destek Canlı Sohbet” ekranı sadece kullanıcı ihtiyaç duyduğunda yüklenmelidir. Bu mimariye Dynamic Feature Delivery (Dinamik Özellik Dağıtımı) diyoruz.

Bu yaklaşım, ana uygulama paketini (base module) son derece hafif tutmamızı sağlar. Ek özellikler, kullanıcı uygulama içinde ilgili butona tıkladığında arka planda Play Core API aracılığıyla indirilir. Bir üretim ERP’sinin mobil istemcisini geliştirirken, detaylı grafiksel raporlama modülünü dinamik modül haline getirdik. Bu sayede ilk indirme boyutunu belirgin ölçüde düşürerek sahadaki operatörlerin hücresel veri üzerinden uygulamayı hızlıca güncellemesini sağladık.

Dinamik bir modül oluşturmak için projenize yeni bir “Dynamic Feature Module” eklemeli ve ana modülün build.gradle dosyasında bunu belirtmelisiniz:

// app/build.gradle
android {
    ...
    dynamicFeatures = [':features:reporting_chart']
}

Dinamik modülün kendi build.gradle dosyasında ise teslimat tipini (delivery type) belirlemeniz gerekir:

// features/reporting_chart/build.gradle
plugins {
    id 'com.android.dynamic-feature'
}

dist {
    moduleName = "reporting_chart"
    onDemand {
        active true // Kullanıcı talep ettiğinde indirilecek
    }
    fusing {
        include true // Dynamic delivery desteklemeyen eski cihazlar için ana pakete dahil et
    }
}

Çalışma zamanında bu modülü yüklemek için Kotlin tarafında SplitInstallManager API’sini kullanırız. Bu kod bloğu, modülün indirilme durumunu dinler ve indirme tamamlandığında kullanıcıyı ilgili ekrana yönlendirir:

val splitInstallManager = SplitInstallManagerFactory.create(context)

val request = SplitInstallRequest.newBuilder()
    .addModule("reporting_chart")
    .build()

splitInstallManager.startInstall(request)
    .addOnSuccessListener { sessionId ->
        // Modül başarıyla indirildi, aktiviteyi başlatabilirsiniz
    }
    .addOnFailureListener { exception ->
        // İndirme hatası yönetimi
    }

CI/CD Pipeline Üzerinde Boyut Regresyonunu Engelleme

Boyut optimizasyonu tek seferlik bir işlem değildir. Ekibe katılan yeni bir yazılımcının projeye eklediği kontrolsüz bir kütüphane veya optimize edilmemiş bir görsel, tüm çabalarınızı bir gecede çöpe atabilir. Bu yüzden boyut takibini otomatikleştirmek ve CI/CD pipeline’ına entegre etmek hayati önem taşır.

GitHub Actions üzerinde her Pull Request açıldığında derlenen paket boyutunu ölçen ve belirlediğiniz limiti aşarsa derlemeyi hata vererek durduran (fail build) basit ama etkili bir workflow hazırladım. Bu adımı kendi projelerimde mutlaka kullanıyorum:

name: Mobile App Size Check

on:
  pull_request:
    branches: [ main, develop ]

jobs:
  size-check:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Build Release Bundle (AAB)
      run: ./gradlew :app:bundleRelease

    - name: Check Bundle Size
      run: |
        # Üretilen AAB dosyasının yolunu tanımla
        BUNDLE_PATH="app/build/outputs/bundle/release/app-release.aab"
        
        # Dosya boyutunu byte cinsinden al
        SIZE_BYTES=$(stat -c%s "$BUNDLE_PATH")
        SIZE_MB=$(echo "scale=2; $SIZE_BYTES / 1048576" | bc)
        
        # Maksimum limit: 20 MB (20971520 Byte)
        MAX_SIZE_BYTES=20971520
        
        echo "Üretilen Paket Boyutu: $SIZE_MB MB ($SIZE_BYTES bytes)"
        
        if [ $SIZE_BYTES -gt $MAX_SIZE_BYTES ]; then
          echo "HATA: Paket boyutu limit sınırını (20 MB) aştı!"
          exit 1
        fi
        echo "Boyut kontrolü başarılı. Limit dahilinde."

Bu kontrol mekanizması sayesinde, kod inceleme (code review) aşamasına gelmeden önce boyutu şişiren değişiklikleri otomatik olarak engellemiş olursunuz. Benzer şekilde, iOS projeleri için Fastlane entegrasyonu ile .ipa boyutunu ölçüp Slack kanalına bildirim atan otomasyonlar da kurabilirsiniz.


Sonuç: Karar Matrisi ve Süreç Takibi

Mobil uygulama boyutu optimizasyonu yaparken her zaman bir trade-off (ödünleşim) dengesi vardır. Kodları çok fazla obfuscate etmek hata loglarının (crash reports) okunmasını zorlaştırır (deobfuscation mapping dosyalarını her build için saklamanız gerekir). Dynamic Delivery kullanmak kod tabanının mimari karmaşıklığını artırır.

Benim bu konudaki net pozisyonum şudur: İlk aşamada R8/ProGuard ve WebP/Vektör dönüşümlerini tavizsiz uygulayın. Bunlar uygulamanın mimarisini bozmadan en yüksek kazancı (%40’a varan küçülme) sağlayan “düşük asılı meyvelerdir” (low-hanging fruits). Eğer hala limitlerin üzerindeyseniz, Dynamic Feature Delivery gibi daha ileri düzey ve mimari değişiklik gerektiren silahlara sarılın.

Sonraki adım olarak, uygulamanızdaki mevcut paket boyutunu analiz etmek için Android Studio içindeki Analyze APK aracını açın ve hangi klasörün en çok yer kapladığını görerek işe başlayın.

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

Sıkça Sorulanlar

Bu makale ile ilgili okurların sorduğu yaygın sorular.

Mobil uygulama boyutu optimizasyonu için hangi araçları kullanmalıyım?
Ben, mobil uygulama boyutu optimizasyonu için çeşitli araçları denedim. APK ve IPA paketlerini küçültmek için R8/ProGuard ayarlarını kullanabilirsiniz. Ayrıca, CI/CD süreçlerini düzenlemek ve otomatik testler yapmak için Jenkins veya GitLab CI/CD gibi araçları kullanabilirsiniz. Benim deneyimim, bu araçların birlikte kullanılmasıyla daha iyi sonuçlar elde edilebileceğini gösteriyor.
Mobil uygulama boyutunu küçültmek için R8/ProGuard ayarlarını nasıl kullanmalıyım?
R8/ProGuard ayarlarını kullanarak, uygulamanızdaki gereksiz kodları ve assetleri kaldırabilirsiniz. Ben, bu ayarları kullanarak, kendi uygulamamdaki boyutu belirgin oranda küçültebildim. Ancak, dikkatli bir şekilde ayarları yapmanız gerekiyor, çünkü yanlış ayarlamalar uygulamanızın çalışmasını etkileyebilir.
Mobil uygulama boyutu optimizasyonu yapılırken hangi tradeoff'lar dikkate alınmalıdır?
Mobil uygulama boyutu optimizasyonu yapılırken, performans ve boyut arasındaki tradeoff'u dikkate almak gerekiyor. Ben, deneyimlerime göre, boyutu küçültmek için bazı optimizasyonları yaparken, uygulamanın performansı biraz düşebilir. Örneğin, assetleri sıkıştırmak, uygulamanın açılış süresini uzatabilir. Bu nedenle, optimizasyonları dikkatli bir şekilde yapmak ve test etmek gerekiyor.
Mobil uygulama boyutu optimizasyonu için kaç sefer deneme yapmak gerekiyor?
Mobil uygulama boyutu optimizasyonu için kaç sefer deneme yapmak gerekiyor, diye bir soru sorduğunuzda, benim deneyimim, bu işlemin iteratif bir süreç olduğunu gösteriyor. Ben, kendi uygulamamı optimize ederken, uzun süren bir süreç geçirdim. Her defasında, küçük değişiklikler yapıp, test ediyordum. Bu şekilde, uygulamanın boyutunu ciddi oranda küçültebildim. Bu nedenle, sabırlı olmak ve iteratif bir yaklaşım benimsemek gerekiyor.
ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Yorumlar

Sunucu Taraflı AI Moderasyon

Yorumlar sunucuda yapay zeka ile denetlenir ve kalıcı olarak saklanır.

?
0/2000

Sunucu taraflı AI denetim

✉️ Ücretsiz · Spam yok · İstediğin an çık

Yeni yazılardan haberdar olun

Yeni içerikler ve teknik notlar e-postanıza gelsin.

  • 📌
    Haftanın en iyisi Sadece okumaya değer tek yazı
  • 🔧
    Alet çantası Bu hafta kullandığım araçlar
  • 🧠
    Perde arkası Blog'a girmeyen notlar

Spam yapmıyoruz. İstediğiniz zaman ayrılabilirsiniz. · Sadece Umami (self-hosted, Google yok) ile takip.

Okuma İstatistikleriniz

0

Yazı Okundu

0dk

Okuma Süresi

0

Gün Serisi

-

Favori Kategori

İlgili Yazılar