1 package com.bumptech.glide.load.engine.cache; 2 3 import android.annotation.TargetApi; 4 import android.app.ActivityManager; 5 import android.content.Context; 6 import android.os.Build; 7 import android.util.DisplayMetrics; 8 import android.util.Log; 9 10 /** 11 * A calculator that tries to intelligently determine cache sizes for a given device based on some constants and the 12 * devices screen density, width, and height. 13 */ 14 public class MemorySizeCalculator { 15 private static final String TAG = "MemorySizeCalculator"; 16 17 static final int BYTES_PER_ARGB_8888_PIXEL = 4; 18 static final int MEMORY_CACHE_TARGET_SCREENS = 2; 19 static final int BITMAP_POOL_TARGET_SCREENS = 3; 20 21 static final float MAX_SIZE_MULTIPLIER = 0.4f; 22 static final float LOW_MEMORY_MAX_SIZE_MULTIPLIER = 0.33f; 23 private final int bitmapPoolSize; 24 private final int memoryCacheSize; 25 26 interface ScreenDimensions { 27 int getWidthPixels(); 28 int getHeightPixels(); 29 } 30 31 public MemorySizeCalculator(Context context) { 32 this((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE), 33 new DisplayMetricsScreenDimensions(context.getResources().getDisplayMetrics())); 34 } 35 36 MemorySizeCalculator(ActivityManager activityManager, ScreenDimensions screenDimensions) { 37 final int maxSize = getMaxSize(activityManager); 38 39 final int screenSize = screenDimensions.getWidthPixels() * screenDimensions.getHeightPixels() 40 * BYTES_PER_ARGB_8888_PIXEL; 41 42 int targetPoolSize = screenSize * BITMAP_POOL_TARGET_SCREENS; 43 int targetMemoryCacheSize = screenSize * MEMORY_CACHE_TARGET_SCREENS; 44 45 if (targetMemoryCacheSize + targetPoolSize <= maxSize) { 46 memoryCacheSize = targetMemoryCacheSize; 47 bitmapPoolSize = targetPoolSize; 48 } else { 49 int part = Math.round((float) maxSize / (BITMAP_POOL_TARGET_SCREENS + MEMORY_CACHE_TARGET_SCREENS)); 50 memoryCacheSize = part * MEMORY_CACHE_TARGET_SCREENS; 51 bitmapPoolSize = part * BITMAP_POOL_TARGET_SCREENS; 52 } 53 54 if (Log.isLoggable(TAG, Log.DEBUG)) { 55 Log.d(TAG, "Calculated memory cache size: " + toMb(memoryCacheSize) + " pool size: " + toMb(bitmapPoolSize) 56 + " memory class limited? " + (targetMemoryCacheSize + targetPoolSize > maxSize) + " max size: " 57 + toMb(maxSize) + " memoryClass: " + activityManager.getMemoryClass() + " isLowMemoryDevice: " 58 + isLowMemoryDevice(activityManager)); 59 } 60 } 61 62 /** 63 * Returns the recommended memory cache size for the device it is run on in bytes. 64 */ 65 public int getMemoryCacheSize() { 66 return memoryCacheSize; 67 } 68 69 /** 70 * Returns the recommended bitmap pool size for the device it is run on in bytes. 71 */ 72 public int getBitmapPoolSize() { 73 return bitmapPoolSize; 74 } 75 76 private static int getMaxSize(ActivityManager activityManager) { 77 final int memoryClassBytes = activityManager.getMemoryClass() * 1024 * 1024; 78 final boolean isLowMemoryDevice = isLowMemoryDevice(activityManager); 79 return Math.round(memoryClassBytes 80 * (isLowMemoryDevice ? LOW_MEMORY_MAX_SIZE_MULTIPLIER : MAX_SIZE_MULTIPLIER)); 81 } 82 83 private static int toMb(int bytes) { 84 return bytes / (1024 * 1024); 85 } 86 87 @TargetApi(Build.VERSION_CODES.KITKAT) 88 private static boolean isLowMemoryDevice(ActivityManager activityManager) { 89 final int sdkInt = Build.VERSION.SDK_INT; 90 return sdkInt < Build.VERSION_CODES.HONEYCOMB 91 || (sdkInt >= Build.VERSION_CODES.KITKAT && activityManager.isLowRamDevice()); 92 } 93 94 private static class DisplayMetricsScreenDimensions implements ScreenDimensions { 95 private final DisplayMetrics displayMetrics; 96 97 public DisplayMetricsScreenDimensions(DisplayMetrics displayMetrics) { 98 this.displayMetrics = displayMetrics; 99 } 100 101 @Override 102 public int getWidthPixels() { 103 return displayMetrics.widthPixels; 104 } 105 106 @Override 107 public int getHeightPixels() { 108 return displayMetrics.heightPixels; 109 } 110 } 111 } 112