private long getFreeMemoryCacheBytes() { return memoryCache.getMaxSize() - memoryCache.getCurrentSize(); }
/** * Clears as much memory as possible. * * @see android.content.ComponentCallbacks#onLowMemory() * @see android.content.ComponentCallbacks2#onLowMemory() */ public void clearMemory() { // Engine asserts this anyway when removing resources, fail faster and consistently Util.assertMainThread(); // memory cache needs to be cleared before bitmap pool to clear re-pooled Bitmaps too. See #687. memoryCache.clearMemory(); bitmapPool.clearMemory(); arrayPool.clearMemory(); }
@Test public void testAddsBitmapsToBitmapPoolIfMemoryCacheIsFull() { Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); when(cache.getMaxSize()).thenReturn(0L); PreFillType size = new PreFillType.Builder(bitmap.getWidth(), bitmap.getHeight()).setConfig(bitmap.getConfig()) .build(); Map<PreFillType, Integer> allocationOrder = new HashMap<>(); allocationOrder.put(size, 1); getHandler(allocationOrder).run(); verify(cache, never()).put(any(Key.class), anyResource()); // TODO(b/20335397): This code was relying on Bitmap equality which Robolectric removed // verify(pool).put(eq(bitmap)); // assertThat(addedBitmaps).containsExactly(bitmap); }
@Test public void testPreventEviction() { final MemoryCache cache = new LruResourceCache(100); final Resource<?> first = getResource(30); final Key firstKey = new MockKey(); cache.put(firstKey, first); Resource<?> second = getResource(30); Key secondKey = new MockKey(); cache.put(secondKey, second); Resource<?> third = getResource(30); Key thirdKey = new MockKey(); cache.put(thirdKey, third); cache.setResourceRemovedListener(new ResourceRemovedListener() { @Override public void onResourceRemoved(@NonNull Resource<?> removed) { if (removed == first) { cache.put(firstKey, first); } } }); // trims from 100 to 50, having 30+30+30 items, it should trim to 1 item cache.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); // and that 1 item must be first, because it's forced to return to cache in the listener @SuppressWarnings("unchecked") LruCache<Key, Resource<?>> lruCache = (LruCache<Key, Resource<?>>) cache; assertTrue(lruCache.contains(firstKey)); assertFalse(lruCache.contains(secondKey)); assertFalse(lruCache.contains(thirdKey)); }
private EngineResource<?> getEngineResourceFromCache(Key key) { Resource<?> cached = cache.remove(key); final EngineResource<?> result; if (cached == null) { result = null; } else if (cached instanceof EngineResource) { // Save an object allocation if we've cached an EngineResource (the typical case). result = (EngineResource<?>) cached; } else { result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/); } return result; }
@Override public void onResourceRemoved(@NonNull Resource<?> removed) { if (removed == first) { cache.put(firstKey, first); } } });
/** * Adjusts Glide's current and maximum memory usage based on the given {@link MemoryCategory}. * * <p> The default {@link MemoryCategory} is {@link MemoryCategory#NORMAL}. * {@link MemoryCategory#HIGH} increases Glide's maximum memory usage by up to 50% and * {@link MemoryCategory#LOW} decreases Glide's maximum memory usage by 50%. This method should be * used to temporarily increase or decrease memory usage for a single Activity or part of the app. * Use {@link GlideBuilder#setMemoryCache(MemoryCache)} to put a permanent memory size if you want * to change the default. </p> * * @return the previous MemoryCategory used by Glide. */ @SuppressWarnings("WeakerAccess") // Public API @NonNull public MemoryCategory setMemoryCategory(@NonNull MemoryCategory memoryCategory) { // Engine asserts this anyway when removing resources, fail faster and consistently Util.assertMainThread(); // memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687. memoryCache.setSizeMultiplier(memoryCategory.getMultiplier()); bitmapPool.setSizeMultiplier(memoryCategory.getMultiplier()); MemoryCategory oldCategory = this.memoryCategory; this.memoryCategory = memoryCategory; return oldCategory; }
/** * Clears some memory with the exact amount depending on the given level. * * @see android.content.ComponentCallbacks2#onTrimMemory(int) */ public void trimMemory(int level) { // Engine asserts this anyway when removing resources, fail faster and consistently Util.assertMainThread(); // memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687. memoryCache.trimMemory(level); bitmapPool.trimMemory(level); arrayPool.trimMemory(level); }
@Test public void testEngineAddedAsListenerToMemoryCache() { harness.getEngine(); verify(harness.cache).setResourceRemovedListener(eq(harness.getEngine())); }
@Before public void setUp() { MockitoAnnotations.initMocks(this); when(pool.getMaxSize()).thenReturn(poolSize); when(pool.getDirty(anyInt(), anyInt(), any(Bitmap.Config.class))) .thenAnswer(new CreateBitmap()); when(cache.getMaxSize()).thenReturn(cacheSize); bitmapPreFiller = new BitmapPreFiller(cache, pool, DecodeFormat.DEFAULT); }
@Test public void testResourceIsReturnedFromCacheIfPresent() { when(harness.cache.remove(eq(harness.cacheKey))).thenReturn(harness.resource); harness.doLoad(); verify(harness.cb).onResourceReady(eq(harness.resource), eq(DataSource.MEMORY_CACHE)); }
@Override public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) { activeResources.deactivate(cacheKey); if (resource.isCacheable()) { cache.put(cacheKey, resource); } else { resourceRecycler.recycle(resource); } }
@Test public void testCanIncreaseMemoryCategory() { MemoryCategory memoryCategory = MemoryCategory.NORMAL; Glide glide = new GlideBuilder() .setBitmapPool(bitmapPool) .setMemoryCache(memoryCache) .build(context); glide.setMemoryCategory(memoryCategory); verify(memoryCache).setSizeMultiplier(eq(memoryCategory.getMultiplier())); verify(bitmapPool).setSizeMultiplier(eq(memoryCategory.getMultiplier())); MemoryCategory newMemoryCategory = MemoryCategory.HIGH; MemoryCategory oldMemoryCategory = glide.setMemoryCategory(newMemoryCategory); assertEquals(memoryCategory, oldMemoryCategory); verify(memoryCache).setSizeMultiplier(eq(newMemoryCategory.getMultiplier())); verify(bitmapPool).setSizeMultiplier(eq(newMemoryCategory.getMultiplier())); }
@Test public void testTrimMemory() { Glide glide = new GlideBuilder() .setBitmapPool(bitmapPool) .setMemoryCache(memoryCache) .build(context); final int level = 123; glide.trimMemory(level); verify(bitmapPool).trimMemory(eq(level)); verify(memoryCache).trimMemory(eq(level)); }
cache.setResourceRemovedListener(this);
@Test public void testAllocationOrderRoundRobinsDifferentSizes() { when(pool.getMaxSize()).thenReturn(defaultBitmapSize); when(cache.getMaxSize()).thenReturn(defaultBitmapSize); PreFillType smallWidth = new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT) .setConfig(defaultBitmapConfig).build(); PreFillType smallHeight = new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 2) .setConfig(defaultBitmapConfig).build(); PreFillQueue allocationOrder = bitmapPreFiller.generateAllocationOrder(smallWidth, smallHeight); List<PreFillType> attributes = new ArrayList<>(); while (!allocationOrder.isEmpty()) { attributes.add(allocationOrder.remove()); } // Either width, height, width, height or height, width, height, width. try { assertThat(attributes).containsExactly(smallWidth, smallHeight, smallWidth, smallHeight) .inOrder(); } catch (AssertionError e) { assertThat(attributes).containsExactly(smallHeight, smallWidth, smallHeight, smallWidth) .inOrder(); } }
@VisibleForTesting PreFillQueue generateAllocationOrder(PreFillType... preFillSizes) { final long maxSize = memoryCache.getMaxSize() - memoryCache.getCurrentSize() + bitmapPool.getMaxSize(); int totalWeight = 0; for (PreFillType size : preFillSizes) { totalWeight += size.getWeight(); } final float bytesPerWeight = maxSize / (float) totalWeight; Map<PreFillType, Integer> attributeToCount = new HashMap<>(); for (PreFillType size : preFillSizes) { int bytesForSize = Math.round(bytesPerWeight * size.getWeight()); int bytesPerBitmap = getSizeInBytes(size); int bitmapsForSize = bytesForSize / bytesPerBitmap; attributeToCount.put(size, bitmapsForSize); } return new PreFillQueue(attributeToCount); }
@Test public void testAddsBitmapsToMemoryCacheIfMemoryCacheHasEnoughSpaceRemaining() { Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); when(cache.getMaxSize()).thenReturn(Long.valueOf(Util.getBitmapByteSize(bitmap))); PreFillType size = new PreFillType.Builder(bitmap.getWidth(), bitmap.getHeight()).setConfig(bitmap.getConfig()) .build(); Map<PreFillType, Integer> allocationOrder = new HashMap<>(); allocationOrder.put(size, 1); getHandler(allocationOrder).run(); verify(cache).put(any(Key.class), anyResource()); verify(pool, never()).put(any(Bitmap.class)); // TODO(b/20335397): This code was relying on Bitmap equality which Robolectric removed // assertThat(addedBitmaps).containsExactly(bitmap); }
@Test public void testCacheIsCheckedIfMemoryCacheable() { when(harness.cache.remove(eq(harness.cacheKey))).thenReturn(harness.resource); harness.doLoad(); verify(harness.cb).onResourceReady(eq(harness.resource), eq(DataSource.MEMORY_CACHE)); }
@Test public void testResourceIsNotAddedToCacheOnReleasedIfNotCacheable() { when(harness.resource.isCacheable()).thenReturn(false); harness.getEngine().onResourceReleased(harness.cacheKey, harness.resource); verify(harness.cache, never()).put(eq(harness.cacheKey), eq(harness.resource)); }