提交 98389d82 编写于 作者: S Sam Judd

Make MemoryCache use Resource not EngineResource.

上级 48be324f
......@@ -17,7 +17,6 @@ import com.bumptech.glide.load.Encoder;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.ResourceEncoder;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.engine.EngineResource;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.engine.cache.DiskCache;
......@@ -256,7 +255,7 @@ public class GlideTest {
requestManager.load(file).into(target);
requestManager.load(file).into(imageView);
verify(target).onResourceReady(any(EngineResource.class), any(GlideAnimation.class));
verify(target).onResourceReady(any(Resource.class), any(GlideAnimation.class));
verify(target).setRequest((Request) notNull());
assertNotNull(imageView.getDrawable());
......
package com.bumptech.glide.load.engine;
import android.os.Looper;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.Encoder;
import com.bumptech.glide.load.Key;
......@@ -15,10 +14,11 @@ import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
import com.bumptech.glide.request.ResourceCallback;
import com.bumptech.glide.tests.BackgroundUtil;
import com.bumptech.glide.tests.GlideShadowLooper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
......@@ -45,6 +45,7 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
......@@ -126,7 +127,7 @@ public class EngineTest {
@Test
public void testResourceIsReturnedFromActiveResourcesIfPresent() {
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource>(harness.resource));
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource<?>>(harness.resource));
harness.doLoad();
......@@ -135,7 +136,7 @@ public class EngineTest {
@Test
public void testResourceIsNotReturnedFromActiveResourcesIfRefIsCleared() {
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource>(null));
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource<?>>(null));
harness.doLoad();
......@@ -144,7 +145,7 @@ public class EngineTest {
@Test
public void testKeyIsRemovedFromActiveResourcesIfRefIsCleared() {
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource>(null));
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource<?>>(null));
harness.doLoad();
......@@ -153,7 +154,7 @@ public class EngineTest {
@Test
public void testResourceIsAcquiredIfReturnedFromActiveResources() {
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource>(harness.resource));
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource<?>>(harness.resource));
harness.doLoad();
......@@ -162,7 +163,7 @@ public class EngineTest {
@Test
public void testNewLoadIsNotStartedIfResourceIsActive() {
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource>(harness.resource));
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource<?>>(harness.resource));
harness.doLoad();
......@@ -171,7 +172,7 @@ public class EngineTest {
@Test
public void testNullLoadStatusIsReturnedIfResourceIsActive() {
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource>(harness.resource));
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource<?>>(harness.resource));
assertNull(harness.doLoad());
}
......@@ -180,7 +181,7 @@ public class EngineTest {
public void testActiveResourcesIsNotCheckedIfReturnedFromCache() {
when(harness.cache.remove(eq(harness.cacheKey))).thenReturn(harness.resource);
EngineResource other = mock(EngineResource.class);
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource>(other));
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource<?>>(other));
harness.doLoad();
......@@ -206,6 +207,28 @@ public class EngineTest {
verify(harness.cb).onResourceReady(eq(harness.resource));
}
@Test
public void testHandlesNonEngineResourcesFromCacheIfPresent() {
final Object expected = new Object();
Resource fromCache = mock(Resource.class);
when(fromCache.get()).thenReturn(expected);
when(harness.cache.remove(eq(harness.cacheKey))).thenReturn(fromCache);
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
Resource resource = (Resource) invocationOnMock.getArguments()[0];
assertEquals(expected, resource.get());
return null;
}
}).when(harness.cb).onResourceReady(any(Resource.class));
harness.doLoad();
verify(harness.cb).onResourceReady(any(Resource.class));
}
@Test
public void testResourceIsAddedToActiveResourceIfReturnedFromCache() {
when(harness.cache.remove(eq(harness.cacheKey))).thenReturn(harness.resource);
......@@ -280,7 +303,7 @@ public class EngineTest {
public void testResourceIsAddedToActiveResourcesOnEngineComplete() {
harness.engine.onEngineJobComplete(harness.cacheKey, harness.resource);
WeakReference<EngineResource> resourceRef = harness.activeResources.get(harness.cacheKey);
WeakReference<EngineResource<?>> resourceRef = harness.activeResources.get(harness.cacheKey);
assertEquals(harness.resource, resourceRef.get());
}
......@@ -328,14 +351,27 @@ public class EngineTest {
@Test
public void testResourceIsAddedToCacheOnReleased() {
final Object expected = new Object();
when(harness.resource.isCacheable()).thenReturn(true);
when(harness.resource.get()).thenReturn(expected);
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
Resource<?> resource = (Resource<?>) invocationOnMock.getArguments()[1];
assertEquals(expected, resource.get());
return null;
}
}).when(harness.cache).put(eq(harness.cacheKey), any(Resource.class));
harness.engine.onResourceReleased(harness.cacheKey, harness.resource);
verify(harness.cache).put(eq(harness.cacheKey), eq(harness.resource));
verify(harness.cache).put(eq(harness.cacheKey), any(Resource.class));
}
@Test
public void testResourceIsNotAddedToCacheOnReleasedIfNotCacheable() {
when(harness.resource.isCacheable()).thenReturn(false);
harness.resource.setCacheable(false);
harness.engine.onResourceReleased(harness.cacheKey, harness.resource);
verify(harness.cache, never()).put(eq(harness.cacheKey), eq(harness.resource));
......@@ -344,7 +380,7 @@ public class EngineTest {
@Test
public void testResourceIsRecycledIfNotCacheableWhenReleased() {
ShadowLooper shadowLooper = Robolectric.shadowOf(Looper.getMainLooper());
when(harness.resource.isCacheable()).thenReturn(false);
harness.resource.setCacheable(true);
shadowLooper.pause();
harness.engine.onResourceReleased(harness.cacheKey, harness.resource);
verify(harness.resource, never()).recycle();
......@@ -354,7 +390,7 @@ public class EngineTest {
@Test
public void testResourceIsRemovedFromActiveResourcesWhenReleased() {
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource>(harness.resource));
harness.activeResources.put(harness.cacheKey, new WeakReference<EngineResource<?>>(harness.resource));
harness.engine.onResourceReleased(harness.cacheKey, harness.resource);
......@@ -438,7 +474,8 @@ public class EngineTest {
Map<Key, ResourceRunner> runners = new HashMap<Key, ResourceRunner>();
Transformation transformation = mock(Transformation.class);
ResourceRunnerFactory factory = mock(ResourceRunnerFactory.class);
Map<Key, WeakReference<EngineResource>> activeResources = new HashMap<Key, WeakReference<EngineResource>>();
Map<Key, WeakReference<EngineResource<?>>> activeResources =
new HashMap<Key, WeakReference<EngineResource<?>>>();
Encoder<Object> sourceEncoder = mock(Encoder.class);
DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.RESULT;
......@@ -453,7 +490,6 @@ public class EngineTest {
public EngineTestHarness() {
when(resource.isCacheable()).thenReturn(true);
when(keyFactory.buildKey(anyString(), anyInt(), anyInt(), any(ResourceDecoder.class),
any(ResourceDecoder.class), any(Transformation.class), any(ResourceEncoder.class),
any(ResourceTranscoder.class), any(Encoder.class))).thenReturn(cacheKey);
......
......@@ -2,7 +2,7 @@ package com.bumptech.glide.load.engine.cache;
import android.content.ComponentCallbacks2;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.engine.EngineResource;
import com.bumptech.glide.load.engine.Resource;
import org.junit.Test;
import java.io.UnsupportedEncodingException;
......@@ -20,8 +20,8 @@ import static org.mockito.Mockito.when;
public class LruResourceCacheTest {
private static class TrimClearMemoryCacheHarness {
LruResourceCache resourceCache = new LruResourceCache(100);
EngineResource first = mock(EngineResource.class);
EngineResource second = mock(EngineResource.class);
Resource first = mock(Resource.class);
Resource second = mock(Resource.class);
ResourceRemovedListener listener = mock(ResourceRemovedListener.class);
......@@ -67,7 +67,7 @@ public class LruResourceCacheTest {
@Test
public void testResourceRemovedListenerIsNotifiedWhenResourceIsRemoved() {
LruResourceCache resourceCache = new LruResourceCache(100);
EngineResource resource = mock(EngineResource.class);
Resource resource = mock(Resource.class);
when(resource.getSize()).thenReturn(200);
ResourceRemovedListener listener = mock(ResourceRemovedListener.class);
......@@ -81,17 +81,17 @@ public class LruResourceCacheTest {
@Test
public void testSizeIsBasedOnResource() {
LruResourceCache resourceCache = new LruResourceCache(100);
EngineResource first = getResource(50);
Resource first = getResource(50);
MockKey firstKey = new MockKey();
resourceCache.put(firstKey, first);
EngineResource second = getResource(50);
Resource second = getResource(50);
MockKey secondKey = new MockKey();
resourceCache.put(secondKey, second);
assertTrue(resourceCache.contains(firstKey));
assertTrue(resourceCache.contains(secondKey));
EngineResource third = getResource(50);
Resource third = getResource(50);
MockKey thirdKey = new MockKey();
resourceCache.put(thirdKey, third);
......@@ -100,8 +100,8 @@ public class LruResourceCacheTest {
assertTrue(resourceCache.contains(thirdKey));
}
private EngineResource getResource(int size) {
EngineResource resource = mock(EngineResource.class);
private Resource getResource(int size) {
Resource resource = mock(Resource.class);
when(resource.getSize()).thenReturn(size);
return resource;
}
......
......@@ -36,8 +36,8 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
private final ResourceRunnerFactory factory;
private final EngineKeyFactory keyFactory;
private final MemoryCache cache;
private final Map<Key, WeakReference<EngineResource>> activeResources;
private final ReferenceQueue<EngineResource> resourceReferenceQueue;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue;
private final Handler mainHandler;
/**
......@@ -64,11 +64,11 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
Engine(ResourceRunnerFactory factory, MemoryCache cache, DiskCache diskCache, ExecutorService resizeService,
ExecutorService diskCacheService, Map<Key, ResourceRunner> runners, EngineKeyFactory keyFactory,
Map<Key, WeakReference<EngineResource>> activeResources) {
Map<Key, WeakReference<EngineResource<?>>> activeResources) {
this.cache = cache;
if (activeResources == null) {
activeResources = new HashMap<Key, WeakReference<EngineResource>>();
activeResources = new HashMap<Key, WeakReference<EngineResource<?>>>();
}
this.activeResources = activeResources;
......@@ -88,7 +88,7 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
}
this.factory = factory;
resourceReferenceQueue = new ReferenceQueue<EngineResource>();
resourceReferenceQueue = new ReferenceQueue<EngineResource<?>>();
MessageQueue queue = Looper.myQueue();
queue.addIdleHandler(new RefQueueIdleHandler(activeResources, resourceReferenceQueue));
cache.setResourceRemovedListener(this);
......@@ -148,7 +148,7 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
EngineKey key = keyFactory.buildKey(id, width, height, cacheDecoder, decoder, transformation, encoder,
transcoder, sourceEncoder);
EngineResource cached = cache.remove(key);
EngineResource<?> cached = getFromCache(key);
if (cached != null) {
cached.acquire(1);
activeResources.put(key, new ResourceWeakReference(key, cached, resourceReferenceQueue));
......@@ -159,9 +159,9 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
return null;
}
WeakReference<EngineResource> activeRef = activeResources.get(key);
WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
if (activeRef != null) {
EngineResource active = activeRef.get();
EngineResource<?> active = activeRef.get();
if (active != null) {
active.acquire(1);
cb.onResourceReady(active);
......@@ -197,6 +197,22 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
return new LoadStatus(cb, runner.getJob());
}
@SuppressWarnings("unchecked")
private EngineResource<?> getFromCache(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);
}
return result;
}
public void release(Resource resource) {
if (resource instanceof EngineResource) {
((EngineResource) resource).release();
......@@ -205,7 +221,6 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
}
}
@SuppressWarnings("unchecked")
@Override
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
......@@ -227,7 +242,7 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
}
@Override
public void onResourceRemoved(final EngineResource resource) {
public void onResourceRemoved(final Resource<?> resource) {
recycleResource(resource);
}
......@@ -241,7 +256,7 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
}
}
private void recycleResource(EngineResource resource) {
private void recycleResource(Resource<?> resource) {
// If a resource has sub-resources, releasing a sub resource can cause it's parent to be synchronously
// evicted which leads to a recycle loop when the parent the releases it's children. Posting breaks this loops.
mainHandler.obtainMessage(ResourceRecyclerCallback.RECYCLE_RESOURCE, resource).sendToTarget();
......@@ -261,10 +276,11 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
}
}
private static class ResourceWeakReference extends WeakReference<EngineResource> {
private static class ResourceWeakReference extends WeakReference<EngineResource<?>> {
private final Key key;
public ResourceWeakReference(Key key, EngineResource r, ReferenceQueue<? super EngineResource> q) {
public ResourceWeakReference(Key key, EngineResource<?> r,
ReferenceQueue<? super EngineResource<?>> q) {
super(r, q);
this.key = key;
}
......@@ -272,11 +288,11 @@ public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedLis
// Responsible for cleaning up the active resource map by remove weak references that have been cleared.
private static class RefQueueIdleHandler implements MessageQueue.IdleHandler {
private Map<Key, WeakReference<EngineResource>> activeResources;
private ReferenceQueue<EngineResource> queue;
private Map<Key, WeakReference<EngineResource<?>>> activeResources;
private ReferenceQueue<EngineResource<?>> queue;
public RefQueueIdleHandler(Map<Key, WeakReference<EngineResource>> activeResources,
ReferenceQueue<EngineResource> queue) {
public RefQueueIdleHandler(Map<Key, WeakReference<EngineResource<?>>> activeResources,
ReferenceQueue<EngineResource<?>> queue) {
this.activeResources = activeResources;
this.queue = queue;
}
......
......@@ -9,14 +9,15 @@ import com.bumptech.glide.load.Key;
*
* @param <Z> The type of data returned by the wrapped {@link Resource}.
*/
public class EngineResource<Z> implements Resource<Z> {
class EngineResource<Z> implements Resource<Z> {
private final Resource<Z> resource;
private volatile int acquired;
private volatile boolean isRecycled;
private ResourceListener listener;
private Key key;
private boolean isCacheable;
private volatile int acquired;
private volatile boolean isRecycled;
interface ResourceListener {
public void onResourceReleased(Key key, EngineResource<?> resource);
}
......@@ -48,23 +49,6 @@ public class EngineResource<Z> implements Resource<Z> {
return resource.getSize();
}
/**
* Cleans up and recycles internal resources.
*
* <p>
* It is only safe to call this method if there are no current resource consumers and if this method has not
* yet been called. Typically this occurs at one of two times:
* <ul>
* <li>During a resource load when the resource is transformed or transcoded before any consumer have
* ever had access to this resource</li>
* <li>After all consumers have released this resource and it has been evicted from the cache</li>
* </ul>
*
* For most users of this class, the only time this method should ever be called is during transformations or
* transcoders, the framework will call this method when all consumers have released this resource and it has
* been evicted from the cache.
* </p>
*/
@Override
public void recycle() {
if (acquired > 0) {
......@@ -88,7 +72,7 @@ public class EngineResource<Z> implements Resource<Z> {
*
* @param times The number of consumers that have just started using the resource.
*/
public void acquire(int times) {
void acquire(int times) {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
......@@ -110,7 +94,7 @@ public class EngineResource<Z> implements Resource<Z> {
* you.
* </p>
*/
public void release() {
void release() {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
......
......@@ -27,12 +27,21 @@ public interface Resource<Z> {
*/
int getSize();
/**
* A method that can be used to clean up or reuse inner resources when this resource is about to be destroyed.
/**
* Cleans up and recycles internal resources.
*
* <p>
* Must not be called directly and otherwise is guaranteed to only be called at most once. May also never be
* called.
* It is only safe to call this method if there are no current resource consumers and if this method has not
* yet been called. Typically this occurs at one of two times:
* <ul>
* <li>During a resource load when the resource is transformed or transcoded before any consumer have
* ever had access to this resource</li>
* <li>After all consumers have released this resource and it has been evicted from the cache</li>
* </ul>
*
* For most users of this class, the only time this method should ever be called is during transformations or
* transcoders, the framework will call this method when all consumers have released this resource and it has
* been evicted from the cache.
* </p>
*/
void recycle();
......
package com.bumptech.glide.load.engine.cache;
import android.annotation.SuppressLint;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.engine.EngineResource;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.util.LruCache;
/**
* An LRU in memory cache for {@link com.bumptech.glide.load.engine.Resource}s.
*/
public class LruResourceCache extends LruCache<Key, EngineResource<?>> implements MemoryCache {
public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {
private ResourceRemovedListener listener;
/**
......@@ -27,14 +26,14 @@ public class LruResourceCache extends LruCache<Key, EngineResource<?>> implement
}
@Override
protected void onItemEvicted(Key key, EngineResource<?> item) {
protected void onItemEvicted(Key key, Resource<?> item) {
if (listener != null) {
listener.onResourceRemoved(item);
}
}
@Override
protected int getSize(EngineResource<?> item) {
protected int getSize(Resource<?> item) {
return item.getSize();
}
......@@ -50,6 +49,5 @@ public class LruResourceCache extends LruCache<Key, EngineResource<?>> implement
// Evict oldest half of our bitmap cache
trimToSize(getCurrentSize() / 2);
}
}
}
package com.bumptech.glide.load.engine.cache;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.engine.EngineResource;
import com.bumptech.glide.load.engine.Resource;
/**
* An interface for adding and removing resources from an in memory cache.
......@@ -11,7 +11,7 @@ public interface MemoryCache {
* An interface that will be called whenever a bitmap is removed from the cache.
*/
public interface ResourceRemovedListener {
public void onResourceRemoved(EngineResource<?> removed);
public void onResourceRemoved(Resource<?> removed);
}
/**
......@@ -31,7 +31,7 @@ public interface MemoryCache {
*
* @param key The key.
*/
public EngineResource<?> remove(Key key);
public Resource<?> remove(Key key);
/**
* Add bitmap to the cache with the given key.
......@@ -40,7 +40,7 @@ public interface MemoryCache {
* @param resource The {@link com.bumptech.glide.load.engine.EngineResource} to store.
* @return The old value of key (null if key is not in map).
*/
public EngineResource<?> put(Key key, EngineResource<?> resource);
public Resource<?> put(Key key, Resource<?> resource);
/**
* Set the listener to be called when a bitmap is removed from the cache.
......
package com.bumptech.glide.load.engine.cache;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.engine.EngineResource;
import com.bumptech.glide.load.engine.Resource;
/**
* A simple class that ignores all puts and returns null for all gets.
......@@ -16,12 +16,12 @@ public class MemoryCacheAdapter implements MemoryCache {
}
@Override
public EngineResource<?> remove(Key key) {
public Resource<?> remove(Key key) {
return null;
}
@Override
public EngineResource<?> put(Key key, EngineResource<?> resource) {
public Resource<?> put(Key key, Resource<?> resource) {
listener.onResourceRemoved(resource);
return null;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册