Bit eşlem belleğini yönetmek, görsel açıdan zengin Android uygulamalarında yetersiz bellek (OOM) risklerini azaltmak için kritik öneme sahiptir. Geliştiriciler resim boyutlarını küçültmeleri gerektiğini bilirler ancak uzun vadeli uygulama kararlılığı elde etmek için diğer iki temel unsuru da iyi bilmeleri gerekir: Yeniden Kullanma ve Geri Dönüştürme. Bu oturumda, modern araçlar, Glide ve Coil gibi açık kaynaklı kitaplıklar ve sistem düzeyindeki API'leri kullanarak bellek performansını optimize etmeye yönelik pratik bir kılavuz sunulacak.
Bu oturumda, Android vitals'ı kullanarak gerçek dünyadaki bellek gerilemelerini nasıl teşhis edeceğinizi, Glide ve Coil ile gelişmiş önbelleğe alma (yeniden kullanma) kalıplarını nasıl uygulayacağınızı ve onTrimMemory geri çağırma işlevini kullanarak belleği proaktif olarak nasıl serbest bırakacağınızı (geri dönüştüreceğinizi) öğreneceksiniz.
BitmapRegionDecoder gibi API'leri kullanın.onTrimMemory işleyerek resim belleğini serbest bırakın. Arka planda bellek kullanımının artmasını önlemek için onStop uygulamasında arka plan yoklamasını ve animasyonları durdurun.Transkript
Fung Lam
Evet, öğle yemeğine yaklaşık 1 saat kaldı. Lütfen beni dinlemeye devam edin.
Ben Fung Lam, Londra'da Android geliştirici ilişkileri mühendisiyim.
Evet, teşekkürler.
Başlamadan önce bir soru sorabilir miyim?
Bu yıl kaçınız yeni bir telefon satın aldı?
Vay canına!
Vay canına!
Dürüst ol, Pixel'i satın almazsan seni suçlamam.
Yeni telefon daha pahalı, değil mi?
Evet, bu yüzden bazılarınız yeni telefon satın almadı.
Neden daha pahalı?
Biliyor musunuz?
Hayır.
RAM.
RAM, evet, evet.
Evet, RAM Android için de bizim için de, geliştiriciler için de gerçekten zorlu bir konu.
Bu nedenle bugün, bellek ve RAM ile ilgili zorlukların üstesinden nasıl gelebileceğimizi anlatmak için buradayım.
Daha önce Google I/O ve Google I/O Connect'te bellek hakkında bazı oturumlar düzenlemiştik. Bu oturumlarda işletim sisteminin belleği nasıl tahsis ettiğinden, uygulamanızın profilini nasıl oluşturabileceğinizden, R8 kullanarak kodunuzu nasıl optimize edebileceğinizden bahsetmiştik. Tabii ki o zamanlar Bitmap hakkında da biraz bilgi vermiştim.
Ekran görüntüsünde fotoğraflarımı görebilirsiniz.
Bellek optimizasyonu; Android çalışma zamanı, çöp toplama, performans araçları, nesne bellek ayırma, derleyici gibi farklı alanları kapsayan geniş bir konudur. Bu 30 dakikalık oturumda, bitmap belleğini nasıl optimize edeceğinizle ilgili bilgi vereceğim.
Neden Bitmap diye sorabilirsiniz.
Neden bit eşlem belleğine odaklanmalıyız?
Çünkü bir resim bin kelimeye bedeldir.
Bu doğru, nedenini açıklayayım.
Telefon ekranında, örneğin Pixel 7, Pixel 9 veya Pixel 11 Pro sürümünde ekran çözünürlüğü yaklaşık 2.500 1.200, 1.400'dür.
Bu nedenle, her piksel için 32 bit renk derinliği kullanırız.
Tam ekran görüntüsü için, parmaklarım yetmiyor ama yaklaşık 15 MB, 50 MB bellek gerekir.
İşletim sistemindeki bir kelime 64 bit ise bu 8 bayt demektir.
Bin kelime yalnızca sekiz, 8 MB'tır.
Evet, 8K.
Yani bir resim kesinlikle bin kelimeye bedel.
Evet, bu önemli bir nokta.
Bir resim zaten 15 MB.
Uygulamanızda 10 veya 100 resim varsa ne olur?
Bu oldukça büyük bir sayı.
Yani aslında Bitmap, uygulamanızda her zaman en çok bellek tüketen öğedir.
Resim bellekte çözüldüğünde, bahsettiğim gibi, boyut ve renk derinliği olarak genişler.
Bu, dosya boyutunun JPEG veya PNG biçiminde olmasıyla ilgili değildir.
Hayır, kod çözülür ve bellek kullanılır.
Bu nedenle, yalnızca birkaç tam çözünürlüklü resim, cihazınızın belleğinde yüzlerce megabayt yer kaplayabilir.
İkinci olarak, bellek çok kısıtlı olduğunda işletim sisteminin bellek tüketimini yönetmek için daha fazla güç harcaması gerekir.
Bu durum, kullanıcı arayüzünün sarsıntılı olmasına neden olur.
İşletim sistemi, bellekte yer açmak için uygulamanızı da sonlandırabilir.
Aksi takdirde uygulamanızda "Yetersiz bellek" hatası veya "Yetersiz bellek" istisnası da oluşur.
Neyse ki bu yüzü çizmek genellikle çok az çabayla büyük bir başarı elde etmenizi sağlar.
Bellek kullanımındaki ani artışların çoğu, kaçınılabilir olan yaygın hatalardan kaynaklanır. Bu hataları, resim kalitesinden ödün vermeden düzeltebiliriz.
Artık bit eşlem belleğinin uygulamanız için önemli olduğunu biliyorsunuz. Bu bellek, uygulamanızın sorunsuz çalışmasını sağlamanın yanı sıra kararlılık ve kullanıcıları elde tutma açısından da önemlidir.
Bugün, uygulamanızın sorunsuz çalışmasını sağlayacak beş basit bölümü inceleyeceğiz.
İlk olarak, Google Play teknik kalitesini ve yeni teknik kalite şartını inceleyeceğiz. Bu şart, yakında uygulanacak bellek sınırını ve bu sınırı kolayca kontrol edebileceğiniz yeri içerir.
İkinci olarak, 500 Android uygulamasıyla ilgili bir örnek olay incelemesi paylaşacağım. Bu uygulamalar belleği nasıl kullanıyor? Doğru şekilde mi kullanıyorlar yoksa öğrenebileceğimiz bir şey var mı?
Ardından, bit eşlem bellek kullanımını optimize etme ilkesini göstereceğim.
Son olarak, kendi uygulamanızı kontrol etmenize yardımcı olabilecek bazı araçları paylaşacağız.
Fung Lam
İlk olarak, Google Play'in yeni kalite şartından bahsedelim.
Google Play Console'u ne sıklıkla ziyaret ediyorsunuz?
Her ay mı?
Her hafta mı?
Hiç mi?
Gülüyorsanız evet diyorsunuz demektir, değil mi?
Cevabınız hayır mı?
Hayır.
Android vitals bölümünü ziyaret ettiniz mi?
Evet, evet.
Evet, yani bunu yapmadan önce bir şeyler biliyorsunuz.
Bu nedenle Google Play'de performans eşiğiyle ilgili yeni kalite şartları var.
Android ve Play olarak kullanıcıların uygulamaları kaliteli bir şekilde kullanmasını istiyoruz.
Bu eşik de son kullanıcıya en iyisini sunmaya çalıştığımız bir başka alan.
Basitçe ifade etmek gerekirse, "Uygulamanız arka planda veya gözlemlenebilir ön plan yüzeyinde çalışırken bellek kullanımınızın 90. yüzdelik dilimi bu eşiğin üzerinde olmamalıdır.
Aksi takdirde uygulamanızın Play Store'daki görünürlüğü azalabilir."
Bu politika, Şubat 2027'den itibaren uygulama ve oyunlarda uygulanmaya başlayacak.
Bu eşiğin aşılması, uygulama sağlığı değerlendirmenizi doğrudan etkiler.
Bu nedenle, Google Play Console'a gidip "Bugün 90. yüzdelik dilimim ne?" diye kontrol etmeyi unutmayın.
İyileştirmeyi son tarihten önce yapın.
Bu, animasyondur.
"Google Play Console'a giderseniz Android vitals'ı görebilirsiniz.
Belleğin bir bölümü var.
90. yüzdelik dilimi görebilirsiniz.
Tıkladığınızda zaman içindeki ortalama bellek tüketimini görebilirsiniz.
Ayrıca RAM, form faktörü, ekran yoğunluğu ve işletim sistemi sürümüne göre farklı katmanlar da vardır. Bu katmanlar, belirli giriş seviyesi cihazlarda veya belirli uygulama sürümlerinde bellek darboğazını izole etmenize yardımcı olur.
"Uygulamamın eşiği aştığını söylediğiniz için teşekkürler.
Ama gerçekten neyin yanlış olduğunu bilmiyorum.
Yardımcı olur musunuz?"
Evet, bu yüzden size yardımcı olmak için buradayım.
Bunu sizin için kolaylaştırmaya çalışacağım. Örneğin, televizyon izliyorsunuz ve televizyonunuzda görüntü veya ses yok. Ne yaparsınız?
TV'nizin kullanım kılavuzunu çıkarıp genellikle sorun giderme kılavuzunun bulunduğu son birkaç sayfaya gidersiniz. Burada, "TV'nizden ses gelmiyorsa şu düğmeyi veya bu düğmeyi deneyin." gibi ifadeler yer alır.
Evet, benzer şekilde bugün ben de bazı yaygın sorunlar tespit ettim.
Bu rehberin sorun giderme konusunda size yardımcı olacağını umuyoruz.
Normalde TV kılavuzunu okurken birinci sayfadan başlamamız gerekir. Bu sayfa genellikle TV'nin nasıl çalıştığını anlamanıza yardımcı olacak teşhis araçlarını içerir.
Ancak artık bunların daha az ilgi çekici olduğunu biliyorum. Bu nedenle, bugün lütfen önce sorun giderme adımlarını uygulamama izin verin, daha sonra bu konuya geri döneceğiz.
Dürüst olmak gerekirse TV kılavuzunu gerçekten böyle okuyorum.
Android ekosisteminde en sık karşılaşılan bit eşlem tahsisi ve bit eşlem bellek sorununu ortaya çıkarmak için Android'deki yaklaşık 500 popüler uygulamayı inceledim.
Uygulamayı gerçekten çalıştırıyorum, başlatıyorum, giriş yapıyorum, farklı sayfalara gidiyorum, derinlere iniyorum ve geri dönüyorum.
Uygulamayı farklı RAM'lerde ve farklı cihazlarda da test ediyorum. Örneğin, 2 GB belleğe sahip Android Go cihaz, giriş seviyesi 4 GB, orta seviye 4 GB, 6 GB ila 8 GB veya gerçekten büyük ekranlı, çok iyi ekran kalitesine ve 16 GB belleğe sahip premium katlanabilir cihazlar.
Bu cihazın farklı uygulama durumlarındaki bellek kullanımını ölçtük, yığın dökümünü aldık ve Perfetto AI Skill de dahil olmak üzere farklı araçlar kullanarak ayırmayı analiz ettik. Böylece yaygın anti-kalıbı, yani geniş ölçekte yaptığımız yaygın hatayı belirledik.
Bellek, farklı etkinlik durumlarında bu şekilde değişir.
Bu analizle, büyük boyutlu resimlerden arka plan önbelleklerinin artmasına kadar 15'ten fazla yaygın sorunu tespit ediyoruz.
Bugün paylaşacağımız ve inceleyeceğimiz temel örnek olay bu.
Fung Lam
Evet, şu anda paylaştığım örnek olay incelemesinin tamamı 500 uygulama araştırmasıyla ilgili.
Burada olduğunuza göre hepimiz Android'i seviyoruz, değil mi?
Android'i hepimiz çok seviyoruz.
Diyelim ki aramızda Android'i çok seven ama Android geliştiricisi olmayan biri var.
Bu nedenle, Android figürü siparişi vermek için bir Android uygulaması oluşturdu.
Ancak bazı hatalar var.
İlk hatanın bu olup olmadığına bakalım.
"Ürün sayfasında, farklı Android figürlerini gösteren iki sütunlu bir düzenimiz var.
Her sütun için resim yaklaşık 400 x 400 piksel boyutundadır.
Ancak uygulama, ekranımın ne kadar büyük olduğuna bakılmaksızın sunucudan tek bir resim indiriyor.
3.000 x 3.000 boyutunda bir resim indiriliyor.
Evet, çünkü geliştirici olarak "Android çok güzel, çok muhteşem.
Kullanıcıya en iyisini göstermek istiyorum."
Ancak aslında hayır, çünkü resim görünümünün boyutuyla sınırlıdır.
Bu nedenle, büyük resmi indirseniz bile kullanıcıya en iyi şekilde gösterilmez.
Ayrıca bu büyük resim ek bellek kullanımına neden oldu.
Alt örnekleme yapsak bile bu işlem de ek bellek kullanır.
Bu geliştiriciye sorunu düzeltmesi için nasıl yardımcı olabiliriz?
CDN'yi biliyorsanız CDN'nin kırpma, yeniden boyutlandırma, döndürme, filtre uygulama, kare resmi yuvarlak resme dönüştürme gibi bazı resim işlemlerini yapabildiğini de biliyorsunuzdur.
CDN, HTTP isteğiyle bir şeyleri işleyebilir.
Bu nedenle, sunucu tarafınız yalnızca bir resim barındırıyor olsa bile yeniden boyutlandırılmış bir resmi cihazınıza göndermek için CDN'yi kullanabilirsiniz.
Yani ilk olarak, resmi istediğinizde CDN kullanmaya çalışın. Böylece resmi ImageView boyutunda istersiniz.
Ancak bu mümkün değilse (örneğin, bazen ImageView'dan biraz daha büyük olabilir) kodda yeniden boyutlandırmak için alt örnekleme yapmayı unutmayın.
Bu nedenle, belleğe yüklendiğinde tam çözünürlüklü resim yerine daha küçük bir resim yüklenir.
Coil için bir kod örneği:
// 1. Ask CDN for the exact UI card dimensions (e.g. 540x675)
val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp"
// 2. Enforce exact downsampling + Hardware Bitmap config
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(sizedUrl)
.size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL!
.precision(Precision.EXACT) // Downsample to exact card size
.bitmapConfig(Bitmap.Config.HARDWARE)
.build(),
contentDescription = product.title
)Boyutu, sarı renkle gösterilen ifade olarak iletebilirsiniz.
Evet, boyutu HTTP isteğinde HTTP olarak iletebilirsiniz.
Ardından, vurgulanan kodda, vurguyu görebiliyor musunuz? Evet, Size.ORIGINAL kullanmayın.
Evet, genişlik ve yükseklik değerlerini kullanın. Böylece parametre olarak iletilir.
Resmi tam boyuta küçültmek için hassasiyeti kullanın.
Mümkünse Bitmap.Config.HARDWARE kullanın. Bu yöntem, resim doğrudan VRAM'de depolanacağı için daha az bellek kullanır.
Benzer şekilde, bu uygulamada Android koleksiyon ürünlerinin yeni bir sürümünün yayınlandığını varsayalım. Kullanıcının yeni Android figürünün farklı tasarımlarını görmek için sola ve sağa kaydırabileceği uzun bir resim var.
Ancak burada başka bir sorun daha var.
Uzun resmin büyük bir kısmı ekranın dışında kalır. Yani kullanıcı resmi göremez ancak resim belleğe yüklenir.
Daha iyi yapabilir miyiz?
Evet.
Resmi daha küçük parçalara bölüp yalnızca ekranda gösterilen resmi yükleyebiliriz.
Ekranın dışındakiler yüklenmez veya kaldırılır.
Bu nedenle daha az bellek kullanır.
Benzer şekilde, bu durumda yaklaşık %60 bellek tasarrufu sağlandı.
Elbette daha küçük bir dikdörtgen şeklinde kırparsanız daha fazla tasarruf edersiniz.
Ancak benzer şekilde, kullanıcıya bir özellik sunmak istiyorsanız, örneğin Android heykelciğinin her ayrıntısını görmek için yakınlaştırma yapabiliyorsa, sık yapılan bir hata, yine çok büyük bir resim yüklemektir, böylece tekrar tekrar yakınlaştırılabilir.
Ancak hayır, bunu yapmak zorunda değilsiniz.
Daha iyi bir yöntem, kullanıcı önceden yüklenmiş düşük çözünürlüklü orijinal resmi yakınlaştırdığında ve parmaklarını sıkıştırmayı bıraktığında koordinatı hesaplayıp tam çözünürlüklü resmi yüklemek yerine yalnızca yüksek çözünürlüklü resimden hesaplanan alanı yüklemektir.
Bu nedenle, belleğe yalnızca görünür alanlar yüklenir.
Bunu nasıl yapabilirsiniz?
BitmapRegionDecoder adında bir API var. Bu API, bir resmin belirli bir bölgesini yüklemenize yardımcı olur.
Ayrıca size yardımcı olabilecek bazı araçlar ve kitaplıklar da vardır.
GitHub'da arayabilirsiniz.
Bazı çizgi roman uygulamaları veya haritalar bunu zaten kullanıyor.
Bunları referans olarak kullanabilirsiniz.
Ayrıca biz de insanız, hata yapabiliriz.
Yapay zeka da hata yapabilir.
Hata yapmamak için resim yüklerken de bir sınır ekleyebiliriz.
Örneğin, bu durumda şöyle diyebilirim: "Pencere boyutunu veya ekran boyutunu sınır olarak kullanıp bunu bir min işlevine, istek genişliğinin veya sınır genişliğinin minimum değerine koyalım."
// Query active window bounds instead of hardcoding 1080 / 1920
val bounds = WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(activity).bounds
// Clamp target dimensions so Bitmap NEVER exceeds the active window
val safeWidth = minOf(requestedWidthPx, bounds.width())
val safeHeight = minOf(requestedHeightPx, bounds.height())
val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config)Bu işlev sayesinde, yüklediğim resimler hiçbir zaman sınırın dışına çıkmayacak.
Bu nedenle, ekran çözünürlüğünden daha büyük bir resim yüklemez.
Yalnızca resmin görünür bölgesinin kodunu çözmeniz gerektiğini unutmayın. Bu sayede belleğinizde yer açılır.
Fung Lam
Ayrıca, bu uygulamanın aslında tek bir geliştiriciye ait olmadığını da söyleyebilir.
Aslında birden fazla katkıda bulunan için ve farklı araçlar kullanıyor olabilirler.
Örneğin, bir kısmı "Ah, pardon" ifadesini kullanıyor.
Tekrar başlayayım.
Bu uygulama için de aynı durum geçerli.
Ürün sayfasında bir ürün listesi var ve bu resmi gördüm, alışveriş sepetine ekledim.
Birkaç dakika sonra ödeme sayfasına gittiğimde aynı resim tekrar görünüyor.
Ancak bu gerçekten aynı resim mi yoksa bellekte iki farklı nesne mi?
Evet, sık karşılaşılan hatalardan biri uygulamanın çok fazla görüntü yüklemesi.
Ancak bellekte çok sayıda yinelenen resim de var.
Resim yükleyici kullanıyorsanız önbelleğe alma işlemi için bir anahtar iletebilirsiniz.
Böylece aynı resim kullanıldığında yeni bir nesne oluşturmak yerine önceden ayrılmış Bitmap nesnesini kullanabilirsiniz.
Android Studio'da Memory Profiler'ı kullanarak uygulamanızda yinelenen bit eşlem olup olmadığını kontrol edebilirsiniz.
Bu konuyu Google I/O oturumumda ele almıştım. YouTube'a gidip videoyu izleyebilirsiniz.
Android Studio kullanmıyorsanız yığın dökümünü yakalamak için adb komutlarını kullanabilirsiniz. Ardından, uygulamanızda yinelenen bitmap olup olmadığını sorgulamak için Perfetto yığın grafiği SQL dilini kullanabilirsiniz.
Bu, Coil'deki bir kod örneğidir. Bitmap önbelleğe alma için nasıl anahtar ekleyeceğimi gösterir.
// Strip volatile query tokens (?from=list / ?sig=...) for cache key
val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?"))
// On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List!
AsyncImage(
model = ImageRequest.Builder(context)
.data(productUrl)
.placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse!
.memoryCacheKey(canonicalKey)
.diskCacheKey(productUrl.substringBefore("?"))
.build(),
contentDescription = product.title
)Şimdi ise evet.
Önceki örneğe dönelim.
Uygulamamın geliştiricisi farklı.
Bazı kısımlarında Görünümler kullanılıyor. Bu nedenle, resim yükleyici olarak Glide kullanılıyor.
Bazıları da Compose kullandıkları için Coil'i tercih ediyor.
Az önce bahsettiğiniz resim yükleyici, önbelleğe alma işlemine yardımcı oluyor. Birden fazla resim yükleyicim varsa bu bir sorun teşkil eder mi?
Kesinlikle bir sorun var.
Resim yükleyici için varsayılan önbellek boyutu olarak yığın bellek boyutunun% X'i kullanılır.
Örneğin, bir resim yükleyiciniz varsa belleğinizin% X'i kullanılır.
İki resim yükleyiciniz varsa bellek 2 katına çıkar.
Üç resim yükleyiciniz varsa 3 katı olur.
Resim yükleyiciniz aynı havuzu paylaşmıyorsa da aynı durum geçerlidir.
Örneğin, ürün sayfasında Glide kullanarak bir resim yükledikten sonra ödeme sayfasında aynı resmi Coil kullanarak yüklerim.
Yine de anıda iki resim olur.
Bu iyi değil.
Evet, bu sorunu düzeltmek için ideal olarak önbelleğe alma işlemi için tek bir resim yükleyici veya tek bir paylaşılan havuz kullanırız.
Ancak bu çok fazla mühendislik işi gerektiriyorsa ve bunu yapamıyorsanız ne olur?
Daha kolay bir çözüm, bu resim yükleyicinin resmin yüzde kaçını işleyeceğini, başka bir resim yükleyicinin resmin yüzde kaçını işleyeceğini ölçmek ve bu kodda olduğu gibi farklı resim yükleyiciler için ağırlık ayarlamak olabilir.
val totalBudgetBytes = computeAdaptiveCacheBytes(context)
val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil)
// Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B)
val loaderABudget = (totalBudgetBytes * 0.70).toInt()
val loaderBBudget = (totalBudgetBytes * 0.30).toInt()
// Approach B (Simple Fix without stats): Divide by number of loaders!
val simpleSplitBudget = totalBudgetBytes / numberOfLoaders
MemoryCache.Builder()
.maxSizeBytes(simpleSplitBudget)
.build()loaderABudget için %70, loaderBBudget için %30 değerini ayarlayabilirim. Bu da başka bir yöntem.
Her yükleyicinin ne kadar görüntü işleyeceğini gerçekten bilmiyorsanız yine de bölmek daha iyidir.
Varsayılan değeri kullanmak yerine, iki resim yükleyicim varsa 2'ye, üç resim yükleyicim varsa 3'e bölüyorum. Çünkü varsayılan değer kullanılırsa bellek aşırı kullanılır.
Ürün sayfasında kullanıcının ürünün daha fazlasını görmek için sayfayı kaydırmasını beklediğimizi varsayalım.
Normalde, "Yaklaşık 50 resim veya 50 MB resim önbelleğe alalım." diyebiliriz.
Peki ya uygulama arka plana geçerse?
Uygulama arka plandaysa kullanıcı göz atmıyor olabilir.
Kullanıcı göz atmıyorsa bu miktarda önbellek resmini bellekte tutmaya devam etmeli miyim?
Hayır, yayınlamalıyız.
Evet, onTrimMemory geri çağırma işlevini dikkate almalıyız.
Evet, aslında şu anda birçok uygulama onTrimMemory geri çağırma işlevini uygulamıyor veya dikkate almıyor.
onTrimMemory geri çağırma işlevi için dikkat etmemiz gereken iki sinyal vardır.
Bunlardan biri UI_HIDDEN. Bu, kullanıcının ana sayfa düğmesine basması gibi bir durumdur. Kullanıcı ana sayfa düğmesine bastıktan hemen sonra değil, bir süre sonra gerçekleşir. Yani "Uygulamanız aslında kullanıcıya görünür bir kullanıcı arayüzü olmayan bir durumda." anlamına gelir.
"Kullanıcı uygulamanızı kullanmıyor.
Kaynakları işletim sistemine bırakmanız gerekir."
Bu nedenle önbellek boyutunu küçültmemiz gerekir.
Diğeri ise TRIM_MEMORY_BACKGROUND. Bu, uygulamanın uzun süredir arka planda olduğu ve işletim sisteminin artık kaynaklara ihtiyaç duyduğu anlamına gelir.
Bu aşamada, belleğinizi mümkün olduğunca, neredeyse %100 oranında boşaltmanız gerekir.
Bunu yaparak uygulamanızın işletim sistemi tarafından sonlandırılma riskini de azaltmış olursunuz.
Çünkü "Bellek istiyorlar, veriyorsunuz, sonra işletim sistemi bunu hallediyor." gibi bir durum söz konusu.
Hayırsa işletim sisteminin başka seçeneği yoktur, bir şey yapması gerekir.
Fung Lam
Sık yapılan bir diğer hata da şu: "Uygulamanız arka plana geçtiğinde daha az bellek kullanmalı."
Ancak arka planda daha fazla bellek kullanan uygulamalar gördüm.
Neden?
Bu yüzden araştırma yapıyorum.
Nedenini öğrenmeye çalıştım.
Bunun nedenlerinden biri, "Uygulamalarında animasyon veya bant var.
Örneğin, her 15 saniyede bir yeni bir resim indirir ve kullanıcıya yeni bir resim gösterirler."
Ancak geliştirici, uygulama arka plana geçtiğinde animasyonu durdurmamış.
Yani uygulama arka planda olsa bile kod çalışmaya devam eder, yeni resimler alır, yeni resimlerin kodunu çözer ve daha fazla bellek kullanır.
Bu, uygulamanın yapmaması gereken bir şeydir.
Evvet.
Uygulama arka plana geçtiğinde tüm animasyonları, önbelleğe alma işlemlerini veya alakasız işlemleri durdurmalıyız.
Etkinlik yaşam döngüsünü biliyorsanız onPause, onStop etkinlikleri vardır. Animasyonu durdurmak için onStop, onPause, onDestroyView etkinliklerini uyguladığınızdan emin olun. Evet, resim çekme isteklerini veya kullanıcının dikkatini gerektirmeyen işle ilgili diğer şeyleri durdurmak için.
override fun onStop() {
super.onStop()
// 1. Stop animations so they don't decode next frames in background!
bannerAnimationView.cancelAnimation()
animatedImageDrawable?.stop()
// 2. Cancel next-image buffering / prefetch queues!
nextImagesBufferingJob?.cancel()
Glide.with(this).pauseRequests()
}
override fun onDestroyView() {
super.onDestroyView()
bannerAnimationView.clearAnimation() // Release resources!
}Fung Lam
Sizinle birçok destek kaydı hakkında konuştuğumu biliyorum ancak "15'ten fazla destek kaydım var" demiştim.
Tüm senaryoları sizinle paylaşırsam her şeyi unutabileceğinizi biliyorum.
Eve döndüğünüzde Google'da "Bellek sorunlarımı nasıl düzeltebilirim?" diye arama yaparsınız.
Bu yüzden hatırlamanızı kolaylaştırıyorum.
Evvet.
Bunları ezberlemenize yardımcı olmak için çok iyi bilinen 3R çerçevesiyle ilişkilendireceğim.
Bence herkes bu 3R çerçevesini duymuştur.
İlk yöntem, bir resim yüklenirken bellek kullanımını azaltmaktır.
Elbette, bahsettiğim gibi, sunucudan aldığımız resmin boyutunu küçültebilir, doğru boyutu elde edebiliriz.
Renk derinliğini optimize eder veya mümkünse donanım bit eşlemini kullanırız.
CDN kullanmıyorsanız kullanmayı deneyin.
Bu nedenle, sunucudan resmi aldığımızda yalnızca boyuta en uygun resmi alır.
Yeniden kullanım: Belleğe yüklenen resmin yeniden kullanılabilmesi ve belleğimizde yinelenen bit eşlem resmi olmaması için önbelleği kullanırız.
Ayrıca, kodunuzda birden fazla resim yükleyici kullanmamaya çalışın. Bu, verimliliği düşürür.
Azalt, yeniden kullan ve üçüncüsü neydi?
Biliyor musunuz?
Geri dönüşüm.
Yeniden denemek istiyor musunuz?
Geri dönüşüm, evet, geri dönüşüm.
Evet, ilkokulda kodlama öğrenmesek de azaltmayı, yeniden kullanmayı ve geri dönüştürmeyi öğreniyoruz.
Evet, geri dönüşüm.
Evet, diğer uygulamaların veya işletim sisteminin kullanabilmesi için belleğin nasıl serbest bırakılacağıyla ilgili.
Geri dönüşümle ilgili şeyler var.
İlk olarak, onTrimMemory etkinliğinden bahsetmiştim. Kullanıcı arayüzünüz gizlendiğinde veya OS'nin gerçekten kaynağa ihtiyacı olduğunda OS'nin size göndereceği iki sinyal olduğunu söylemiştim.
Evet, bu isteği yerine getirmeye çalış.
Ayrıca animasyon, resim yükleyici veya ön planda gerçekleşen başka bir işlem varsa onPause, onStop etkinliği sırasında bu işlemi durdurduğunuzdan emin olun.
Bu nedenle azaltın, yeniden kullanın, geri dönüştürün.
Hatırlaması kolay, değil mi?
Fung Lam
Şimdi TV kılavuzunun daha az ilgi çekici kısmına dönüyoruz.
Bu bellek sorununu etkili bir şekilde ölçmek için farklı araç setlerini nasıl kullanırız?
İlk olarak Google Play Console, Android vitals ve Reporting API'den bahsedelim.
Evet, Google Play Console'da 90. yüzdelik dilimi kontrol edin.
Şimdi neredesiniz?
Web sayfasını ziyaret etmiyorsanız Reporting API'yi de kullanabilirsiniz.
Değeri almak için API'yi kullanmaya devam edebilirsiniz.
Evet, eşiğin 200 MB ve 400 MB olduğundan emin olun.
Eşiği aşmadığınızdan emin olun.
Ayrıca, yığın dökümünü yakalamak veya uygulamanızın UI_HIDDEN aşamasında olduğunu simüle etmek için etkinlik göndermek gibi işlemlerde size yardımcı olacak ADB komutları da vardır.
adb shell dumpsys meminfo <pkg> # watch Native Heap!
adb shell am send-trim-memory <pkg> UI_HIDDENAndroid Studio kullanıyorsanız sorun yok.
Yine de bu, sizin için her zaman iyi bir arkadaştır.
Yığın dökümünü yakalamak için Android Studio Profiler'ı kullanabilirsiniz. Böylece, bellek kullanımınızın ve bellek ayırmanın gerçekte ne olduğunu gösteren bir kullanıcı arayüzüne sahip olursunuz.
Ayrıca Android Studio'da Gemini ile yapay zeka veya diğer Android becerileri hakkında da konuşuyoruz.
Yığın dökümünü analiz etmenize yardımcı olur ve Perfetto SQL izlerini kullanarak bit eşlemi bulmanıza yardımcı olur. Böylece önbellek sahibinin kim olduğunu öğrenebilirsiniz.
Resmim Glide ile mi yoksa Coil ile mi yükleniyor?
Uygulamamda başka bellek sızıntısı var mı?
Bazılarınızın "Aslında artık IDE kullanmıyorum.
İstem mühendisiyim.
Yapay zeka ajanıma istem göndererek kod yazdırıyorum.
Evet, biliyorum.
Boş zamanlarımda sezgisel kodlama da yapıyorum.
Bu nedenle, bunu sizinle yalnızca örnek olarak paylaşıyorum.
Evet, bu sadece 500 uygulama araştırmam için kullandığım istemin bir parçası.
Evet, sadece bir kısmını.
Ekranın büyük olmadığını biliyorsunuz.
Daha fazla eklersem neredeyse hiç göremezsiniz.
Ancak bu sayede, yığın dökümü de dahil olmak üzere bellek kullanımını yakalamak, analiz etmek ve bulguları size sunmak için yapay zekayı nasıl kullanabileceğiniz hakkında fikir edinebilirsiniz.
Buraya bir cümle daha ekleyebilirim: "Lütfen kodum için düzeltme kodunu da ver."
Evet, umarım bu, korkularınızı azaltabilir. "Bellek geniş bir alan.
Üniversitede bilgisayar bilimi okurken işletim sistemi veya bilgisayar sistemi mimarisi eğitimi almadım.
Ama aslında bu o kadar korkutucu ve zor değil."
Evet, bu sayede uygulamanızı kolayca analiz edebilirsiniz.
Lütfen deneyin.
Ancak yapay zeka elbette hata yapacaktır. Bu nedenle, bellek kullanımının yanı sıra işletim sisteminin belleği nasıl tahsis ettiğiyle ilgili temel bilgiler hâlâ önemlidir.
Bu nedenle, konuşmama katılın ve YouTube'daki videomuzu izleyin.
Fung Lam
Özetleyecek olursak, bazı çıkarımlarımız var.
İlk olarak, azaltın.
Resmin doğru boyutta olduğundan emin olun.
Her şeyi belleğe yüklememek için resmi daha küçük resimlere bölersiniz. Evet, yalnızca kullanıcıya görünen resmi belleğe yüklemek için.
Mümkün olduğunda donanım bitmapConfig veya RGB_565 kullanın.
Yeniden kullanın. Bu nedenle, bir önbellek stratejiniz olduğundan emin olun ve bellekteki yinelenen bit eşlem sayısını azaltmak için resim yükleyiciyi kullanın.
Elbette, bu resim yükleyicilerin varsayılan önbellek boyutunun üzerine yazmaya çalışsanız bile birden fazla Bitmap resmi kullanmayın.
Yeniden kullanmıyor, geri dönüştürmüyoruz.
Evvet.
Uygulamanızın bellek yönetimi, uygulama durumuna uyum sağlamalıdır.
Kullanıcı arayüzü gizliyse daha az bellek kullanmanız gerekir.
İşletim sistemi gerçekten belleği serbest bırakmanızı isterse mümkün olduğunca fazla bellek serbest bırakın.
Bitmap, güç veya hesaplama kaynakları fark etmeksizin kaynakları tüketmeyi de bırakır.
Evet, banner zamanlayıcısını, animasyonu ve resim havuzunu durdurun.
Son olarak, izleme yaptığınızdan emin olun.
Google Play Console'u düzenli olarak ziyaret edin.
Bunun bir uzmanlık alanı olduğundan emin olun.
Evet, bu sayede uygulamanızın tüm kullanıcı tabanındaki bellek kullanımının 90. yüzdelik diliminin mevcut durumunu anlayabilirsiniz.
Bu sayede sorunları erkenden tespit edebilirsiniz.
Evvet.
Yerel olarak profil oluşturmak için Android Studio Memory Profiler'ı kullanabilirsiniz.
Ayrıca bazı ADB komutlarımız da var.
Android becerimiz de var.
Ancak gerçek kullanıcılarla ilgili istatistikleri görmek istiyorsanız Google Play Console'u kullanmanız gerekir.
Ayrıca, Google Play Console'a giriş yapmadan API'yi kullanarak verilere erişmek için Google Play Reporting API'yi de kullanabilirsiniz.
Daha fazla bilgi edinmek isterseniz developer.android.com/memory adresini ziyaret edebilirsiniz.
Bu sayede atık miktarını azaltabilir, yeniden kullanabilir ve geri dönüştürebilirsiniz.
Karşılaştığımız zorlukların üstesinden gelmek için birlikte daha iyi bir Android ekosistemi oluşturabileceğimizi umuyorum.
Teşekkür eder, keyifli kodlamalar dileriz.
Kısa süre sonra dışarıda ofis saatlerim olacak. Sorularınız varsa gelebilirsiniz.
Bir sonraki konuşmacının sahneye çıkması biraz zaman alacak. Lütfen bekleyin. Çok teşekkürler.
// 1. Ask CDN for the exact UI card dimensions (e.g. 540x675)
val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp"
// 2. Enforce exact downsampling + Hardware Bitmap config
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(sizedUrl)
.size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL!
.precision(Precision.EXACT) // Downsample to exact card size
.bitmapConfig(Bitmap.Config.HARDWARE)
.build(),
contentDescription = product.title
)// Query active window bounds instead of hardcoding 1080 / 1920
val bounds = WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(activity).bounds
// Clamp target dimensions so Bitmap NEVER exceeds the active window
val safeWidth = minOf(requestedWidthPx, bounds.width())
val safeHeight = minOf(requestedHeightPx, bounds.height())
val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config)// Strip volatile query tokens (?from=list / ?sig=...) for cache key
val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?"))
// On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List!
AsyncImage(
model = ImageRequest.Builder(context)
.data(productUrl)
.placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse!
.memoryCacheKey(canonicalKey)
.diskCacheKey(productUrl.substringBefore("?"))
.build(),
contentDescription = product.title
)val totalBudgetBytes = computeAdaptiveCacheBytes(context)
val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil)
// Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B)
val loaderABudget = (totalBudgetBytes * 0.70).toInt()
val loaderBBudget = (totalBudgetBytes * 0.30).toInt()
// Approach B (Simple Fix without stats): Divide by number of loaders!
val simpleSplitBudget = totalBudgetBytes / numberOfLoaders
MemoryCache.Builder()
.maxSizeBytes(simpleSplitBudget)
.build()override fun onStop() {
super.onStop()
// 1. Stop animations so they don't decode next frames in background!
bannerAnimationView.cancelAnimation()
animatedImageDrawable?.stop()
// 2. Cancel next-image buffering / prefetch queues!
nextImagesBufferingJob?.cancel()
Glide.with(this).pauseRequests()
}
override fun onDestroyView() {
super.onDestroyView()
bannerAnimationView.clearAnimation() // Release resources!
}adb shell dumpsys meminfo <pkg> # watch Native Heap!
adb shell am send-trim-memory <pkg> UI_HIDDEN