1dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddpackage com.bumptech.glide.load.engine.prefill; 2dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 3dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport android.graphics.Bitmap; 4dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport android.os.Handler; 5dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport android.os.Looper; 6dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport android.os.SystemClock; 7dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport android.util.Log; 8f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Judd 9dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport com.bumptech.glide.load.Key; 10dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 11dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport com.bumptech.glide.load.engine.cache.MemoryCache; 12dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport com.bumptech.glide.load.resource.bitmap.BitmapResource; 13dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport com.bumptech.glide.util.Util; 14dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 15dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport java.io.UnsupportedEncodingException; 16dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport java.security.MessageDigest; 17dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport java.util.HashSet; 18dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport java.util.Set; 19dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddimport java.util.concurrent.TimeUnit; 20dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 21dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd/** 22dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * A class that allocates {@link android.graphics.Bitmap Bitmaps} to make sure that the 23dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool} is pre-populated. 24dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * 25dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * <p>By posting to the main thread with backoffs, we try to avoid ANRs when the garbage collector gets into a state 26dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * where a high percentage of {@link Bitmap} allocations trigger a stop the world GC. We try to detect whether or not a 27dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * GC has occurred by only allowing our allocator to run for a limited number of milliseconds. Since the allocations 28dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * themselves very fast, a GC is the most likely reason for a substantial delay. If we detect our allocator has run for 29dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * more than our limit, we assume a GC has occurred, stop the current allocations, and try again after a delay. 30dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd */ 31dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Juddfinal class BitmapPreFillRunner implements Runnable { 32dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private static final String TAG = "PreFillRunner"; 33dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private static final Clock DEFAULT_CLOCK = new Clock(); 34dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 35dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd /** 36dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * The maximum number of millis we can run before posting. Set to match and detect the duration of non concurrent 37dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * GCs. 38dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd */ 39dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd static final long MAX_DURATION_MS = 32; 40dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 41dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd /** 42dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * The amount of time in ms we wait before continuing to allocate after the first GC is detected. 43dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd */ 44dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd static final long INITIAL_BACKOFF_MS = 40; 45dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 46dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd /** 47dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * The amount by which the current backoff time is multiplied each time we detect a GC. 48dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd */ 49dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd static final int BACKOFF_RATIO = 4; 50dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 51dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd /** 52dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * The maximum amount of time in ms we wait before continuing to allocate. 53dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd */ 54dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd static final long MAX_BACKOFF_MS = TimeUnit.SECONDS.toMillis(1); 55dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 56dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private final BitmapPool bitmapPool; 57dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private final MemoryCache memoryCache; 58dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private final PreFillQueue toPrefill; 59dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private final Clock clock; 60dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private final Set<PreFillType> seenTypes = new HashSet<PreFillType>(); 61dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private final Handler handler; 62dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 63dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private long currentDelay = INITIAL_BACKOFF_MS; 64dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private boolean isCancelled; 65dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 66dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd public BitmapPreFillRunner(BitmapPool bitmapPool, MemoryCache memoryCache, PreFillQueue allocationOrder) { 67dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd this(bitmapPool, memoryCache, allocationOrder, DEFAULT_CLOCK, new Handler(Looper.getMainLooper())); 68dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 69dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 70dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd // Visible for testing. 71dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd BitmapPreFillRunner(BitmapPool bitmapPool, MemoryCache memoryCache, PreFillQueue allocationOrder, Clock clock, 72dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd Handler handler) { 73dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd this.bitmapPool = bitmapPool; 74dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd this.memoryCache = memoryCache; 75dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd this.toPrefill = allocationOrder; 76dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd this.clock = clock; 77dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd this.handler = handler; 78dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 79dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 80dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd public void cancel() { 81dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd isCancelled = true; 82dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 83dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 84dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd /** 85dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * Attempts to allocate {@link android.graphics.Bitmap}s and returns {@code true} if there are more 86dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd * {@link android.graphics.Bitmap}s to allocate and {@code false} otherwise. 87dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd */ 88dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private boolean allocate() { 89dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd long start = clock.now(); 90dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd while (!toPrefill.isEmpty() && !isGcDetected(start)) { 91dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd PreFillType toAllocate = toPrefill.remove(); 92dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd Bitmap bitmap = Bitmap.createBitmap(toAllocate.getWidth(), toAllocate.getHeight(), 93dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd toAllocate.getConfig()); 94dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 95dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd // Don't over fill the memory cache to avoid evicting useful resources, but make sure it's not empty so 96dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd // we use all available space. 97dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd if (getFreeMemoryCacheBytes() >= Util.getBitmapByteSize(bitmap)) { 98dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd memoryCache.put(new UniqueKey(), BitmapResource.obtain(bitmap, bitmapPool)); 99dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } else { 100dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd addToBitmapPool(toAllocate, bitmap); 101dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 102dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 103dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 104dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd Log.d(TAG, "allocated [" + toAllocate.getWidth() + "x" + toAllocate.getHeight() + "] " 105dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd + toAllocate.getConfig() + " size: " + Util.getBitmapByteSize(bitmap)); 106dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 107dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 108dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 109dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd return !isCancelled && !toPrefill.isEmpty(); 110dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 111dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 112dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private boolean isGcDetected(long startTimeMs) { 113dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd return clock.now() - startTimeMs >= MAX_DURATION_MS; 114dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 115dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 116dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private int getFreeMemoryCacheBytes() { 117dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd return memoryCache.getMaxSize() - memoryCache.getCurrentSize(); 118dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 119dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 120dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private void addToBitmapPool(PreFillType toAllocate, Bitmap bitmap) { 121dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd // The pool may not move sizes to the front of the LRU on put. Do a get here to make sure the size we're adding 122dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd // is at the front of the queue so that the Bitmap we're adding won't be evicted immediately. 123dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd if (seenTypes.add(toAllocate)) { 124dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd Bitmap fromPool = bitmapPool.get(toAllocate.getWidth(), toAllocate.getHeight(), 125dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd toAllocate.getConfig()); 126dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd if (fromPool != null) { 127dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd bitmapPool.put(fromPool); 128dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 129dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 130dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 131dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd bitmapPool.put(bitmap); 132dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 133dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 134dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd @Override 135dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd public void run() { 136dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd if (allocate()) { 137dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd handler.postDelayed(this, getNextDelay()); 138dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 139dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 140dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 141dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private long getNextDelay() { 142dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd long result = currentDelay; 143dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd currentDelay = Math.min(currentDelay * BACKOFF_RATIO, MAX_BACKOFF_MS); 144dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd return result; 145dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 146dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 147dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd private static class UniqueKey implements Key { 148dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 149dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd @Override 150dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd public void updateDiskCacheKey(MessageDigest messageDigest) throws UnsupportedEncodingException { 151dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd // Do nothing. 152dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 153dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 154dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd 155dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd // Visible for testing. 156dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd static class Clock { 157dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd public long now() { 158dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd return SystemClock.currentThreadTimeMillis(); 159dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 160dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd } 161dd737542dc2f6d58cfb1367929c8d3ebc1eeebf4Sam Judd} 162