2018'in sonu olduğundan işler değişti.
Her şeyden önce: uygulamanızı çalıştırın ve Android Studio'da Android Profiler sekmesini açın. Ne kadar hafıza tükettiğini göreceksiniz, şaşıracaksınız ama çok fazla RAM ayırabiliyor.
Ayrıca burada, bellek yönetiminize derinlemesine bir bakış sağlayabilecek, Bellek Profilcisi'nin nasıl kullanılacağına ilişkin ayrıntılı talimatlar içeren resmi belgelerde harika bir makale bulunmaktadır.
Ancak çoğu durumda, normal Android Profilerınız sizin için yeterli olacaktır.
Genellikle, bir uygulama 50Mb RAM tahsisi ile başlar, ancak hafızaya bazı fotoğrafları yüklemeye başladığınızda anında 90Mb'ye kadar atlar. Önceden yüklenmiş fotoğraflar (her biri 3,5Mb) içeren bir ViewPager ile Activity'i açtığınızda, 190Mb'yi saniyeler içinde kolayca alabilirsiniz.
Ancak bu, bellek yönetimi ile ilgili sorunlarınız olduğu anlamına gelmez.
Verebileceğim en iyi tavsiye, yönergeleri ve en iyi uygulamaları takip etmek, resim yüklemek için en iyi kitaplıkları kullanmak (Glide, Picasso) ve iyi olacaksın.
Ancak bir şeyi uyarlamanız gerekiyorsa ve manuel olarak ne kadar bellek ayırabileceğinizi gerçekten bilmeniz gerekiyorsa, toplam boş bellek elde edebilir ve bunun önceden belirlenmiş bir kısmını (% olarak) hesaplayabilirsiniz. Benim durumumda, şifresi çözülmüş fotoğrafları bellekte önbelleğe almam gerekiyordu, böylece kullanıcı listeyi her kaydırdığında bunların şifresini çözmeme gerek kalmadı.
Bu amaçla kullanıma hazır LruCache sınıfını kullanabilirsiniz . Nesnelerinizin ne kadar bellek ayırdığını (veya örnek sayısını) otomatik olarak izleyen ve kullanım geçmişlerine göre en eskiyi saklamak için en eskisini kaldıran bir önbellek sınıfıdır.
İşte nasıl kullanılacağına dair harika bir öğretici.
Benim durumumda, 2 önbellek örneği oluşturdum: başparmaklar ve ekler için. Tekil erişimle onları statik hale getirdi, böylece uygulama genelinde küresel olarak kullanılabilirler.
önbellek sınıfı:
public class BitmapLruCache extends LruCache<Uri, byte[]> {
private static final float CACHE_PART_FOR_THUMBS_PRC = 0.01f;
private static final float CACHE_PART_FOR_ATTACHMENTS_PRC = 0.03f;
private static BitmapLruCache thumbCacheInstance;
private static BitmapLruCache attachmentCacheInstance;
public static synchronized BitmapLruCache getDecryptedThumbCacheInstance() {
if (thumbCacheInstance == null) {
int cacheSize = getCacheSize(CACHE_PART_FOR_THUMBS_PRC);
thumbCacheInstance = new BitmapLruCache(cacheSize);
return thumbCacheInstance;
} else {
return thumbCacheInstance;
}
}
public static synchronized BitmapLruCache getDecryptedAttachmentCacheInstance() {
if (attachmentCacheInstance == null) {
int cacheSize = getCacheSize(CACHE_PART_FOR_ATTACHMENTS_PRC);
attachmentCacheInstance = new BitmapLruCache(cacheSize);
return attachmentCacheInstance;
} else {
return attachmentCacheInstance;
}
}
private BitmapLruCache(int maxSize) {
super(maxSize);
}
public void addBitmap(Uri uri, byte[] bitmapBytes) {
if (get(uri) == null && bitmapBytes != null)
put(uri, bitmapBytes);
}
public byte[] getBitmap(Uri uri) {
return get(uri);
}
@Override
protected int sizeOf(Uri uri, byte[] bitmapBytes) {
return bitmapBytes.length;
}
}
Mevcut boş RAM'i şu şekilde hesaplıyorum ve bundan ne kadar ısırabilirim:
private static int getCacheSize(float partOfTotalFreeMemoryToUseAsCache){
final long maxMemory = Runtime.getRuntime().maxMemory();
return (int) (maxMemory * partOfTotalFreeMemoryToUseAsCache);
}
Ve Adaptörler'de önbelleğe alınmış görüntüyü almak için bu şekilde kullanıyorum:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
ve arka planda önbelleğe nasıl ayarladığımı (normal AsyncTask):
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
Uygulamam API 19 + 'yı hedeflediğinden cihazlar eski değil ve kullanılabilir RAM'in bu kısımları benim durumumda önbellek için yeterince iyi (% 1 ve% 3).
Eğlenceli gerçek: Android'in uygulamanıza ayrılan bellek miktarını almak için herhangi bir API'si veya başka hack'leri yoktur, çeşitli faktörlere göre anında hesaplanır.
Not: Bir önbelleği tutmak için statik bir sınıf alanı kullanıyorum, ancak en son Android yönergelerine göre, bu amaç için ViewModel mimari bileşeninin kullanılması önerilmektedir .