Engine.java revision b7c7c2b2505f996dbda219faeb0d08dc1c9982d7
1package com.bumptech.glide.load.engine; 2 3import android.os.Handler; 4import android.os.Looper; 5import android.os.MessageQueue; 6import android.util.Log; 7import com.bumptech.glide.Priority; 8import com.bumptech.glide.load.Key; 9import com.bumptech.glide.load.ResourceDecoder; 10import com.bumptech.glide.load.ResourceEncoder; 11import com.bumptech.glide.load.Transformation; 12import com.bumptech.glide.load.data.DataFetcher; 13import com.bumptech.glide.load.engine.cache.DiskCache; 14import com.bumptech.glide.load.engine.cache.MemoryCache; 15import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; 16import com.bumptech.glide.request.ResourceCallback; 17import com.bumptech.glide.util.LogTime; 18 19import java.io.InputStream; 20import java.lang.ref.ReferenceQueue; 21import java.lang.ref.WeakReference; 22import java.util.HashMap; 23import java.util.Map; 24import java.util.concurrent.ExecutorService; 25 26public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedListener, Resource.ResourceListener { 27 private static final String TAG = "Engine"; 28 private final Map<Key, ResourceRunner> runners; 29 private final ResourceRunnerFactory factory; 30 private final KeyFactory keyFactory; 31 private final MemoryCache cache; 32 private final Map<Key, WeakReference<Resource>> activeResources; 33 private final ReferenceQueue<Resource> resourceReferenceQueue; 34 35 public static class LoadStatus { 36 private final EngineJob engineJob; 37 private final ResourceCallback cb; 38 39 public LoadStatus(ResourceCallback cb, EngineJob engineJob) { 40 this.cb = cb; 41 this.engineJob = engineJob; 42 } 43 44 public void cancel() { 45 engineJob.removeCallback(cb); 46 } 47 } 48 49 public Engine(MemoryCache memoryCache, DiskCache diskCache, ExecutorService resizeService, 50 ExecutorService diskCacheService) { 51 this(null, memoryCache, diskCache, resizeService, diskCacheService, null, null, null); 52 } 53 54 Engine(ResourceRunnerFactory factory, MemoryCache cache, DiskCache diskCache, ExecutorService resizeService, 55 ExecutorService diskCacheService, Map<Key, ResourceRunner> runners, KeyFactory keyFactory, 56 Map<Key, WeakReference<Resource>> activeResources) { 57 this.cache = cache; 58 59 if (activeResources == null) { 60 activeResources = new HashMap<Key, WeakReference<Resource>>(); 61 } 62 this.activeResources = activeResources; 63 64 if (keyFactory == null) { 65 keyFactory = new EngineKeyFactory(); 66 } 67 this.keyFactory = keyFactory; 68 69 if (runners == null) { 70 runners = new HashMap<Key, ResourceRunner>(); 71 } 72 this.runners = runners; 73 74 if (factory == null) { 75 factory = new DefaultResourceRunnerFactory(diskCache, new Handler(Looper.getMainLooper()), 76 diskCacheService, resizeService); 77 } 78 this.factory = factory; 79 80 resourceReferenceQueue = new ReferenceQueue<Resource>(); 81 MessageQueue queue = Looper.myQueue(); 82 queue.addIdleHandler(new RefQueueIdleHandler(activeResources, resourceReferenceQueue)); 83 cache.setResourceRemovedListener(this); 84 } 85 86 /** 87 * @param id A unique id for the model, dimensions, cache decoder, decoder, and encoder 88 * @param cacheDecoder 89 * @param fetcher 90 * @param decoder 91 * @param encoder 92 * @param transcoder 93 * @param priority 94 * @param <T> The type of data the resource will be decoded from. 95 * @param <Z> The type of the resource that will be decoded. 96 * @param <R> The type of the resource that will be transcoded from the decoded resource. 97 */ 98 public <T, Z, R> LoadStatus load(String id, int width, int height, ResourceDecoder<InputStream, Z> cacheDecoder, 99 DataFetcher<T> fetcher, ResourceDecoder<T, Z> decoder, Transformation<Z> transformation, 100 ResourceEncoder<Z> encoder, ResourceTranscoder<Z, R> transcoder, Priority priority, 101 boolean isMemoryCacheable, ResourceCallback cb) { 102 long startTime = LogTime.getLogTime(); 103 104 Key key = keyFactory.buildKey(id, width, height, cacheDecoder, decoder, transformation, encoder, transcoder); 105 106 Resource cached = cache.remove(key); 107 if (cached != null) { 108 cached.acquire(1); 109 activeResources.put(key, new ResourceWeakReference(key, cached, resourceReferenceQueue)); 110 cb.onResourceReady(cached); 111 if (Log.isLoggable(TAG, Log.VERBOSE)) { 112 Log.v(TAG, "loaded resource from cache in " + LogTime.getElapsedMillis(startTime)); 113 } 114 return null; 115 } 116 117 WeakReference<Resource> activeRef = activeResources.get(key); 118 if (activeRef != null) { 119 Resource active = activeRef.get(); 120 if (active != null) { 121 active.acquire(1); 122 cb.onResourceReady(active); 123 if (Log.isLoggable(TAG, Log.VERBOSE)) { 124 Log.v(TAG, "loaded resource from active resources in " + LogTime.getElapsedMillis(startTime)); 125 } 126 return null; 127 } else { 128 activeResources.remove(key); 129 } 130 } 131 132 ResourceRunner current = runners.get(key); 133 if (current != null) { 134 EngineJob job = current.getJob(); 135 job.addCallback(cb); 136 if (Log.isLoggable(TAG, Log.VERBOSE)) { 137 Log.v(TAG, "added to existing load in " + LogTime.getElapsedMillis(startTime)); 138 } 139 return new LoadStatus(cb, job); 140 } 141 142 long start = LogTime.getLogTime(); 143 ResourceRunner<Z, R> runner = factory.build(key, width, height, cacheDecoder, fetcher, decoder, transformation, 144 encoder, transcoder, priority, isMemoryCacheable, this); 145 runner.getJob().addCallback(cb); 146 runners.put(key, runner); 147 runner.queue(); 148 if (Log.isLoggable(TAG, Log.VERBOSE)) { 149 Log.v(TAG, "queued new load in " + LogTime.getElapsedMillis(start)); 150 Log.v(TAG, "finished load in engine in " + LogTime.getElapsedMillis(startTime)); 151 } 152 return new LoadStatus(cb, runner.getJob()); 153 } 154 155 @Override 156 public void onEngineJobComplete(Key key, Resource resource) { 157 // A null resource indicates that the load failed, usually due to an exception. 158 if (resource != null) { 159 resource.setResourceListener(key, this); 160 activeResources.put(key, new ResourceWeakReference(key, resource, resourceReferenceQueue)); 161 } 162 runners.remove(key); 163 } 164 165 @Override 166 public void onEngineJobCancelled(Key key) { 167 ResourceRunner runner = runners.remove(key); 168 runner.cancel(); 169 } 170 171 @Override 172 public void onResourceRemoved(Resource resource) { 173 resource.recycle(); 174 } 175 176 @Override 177 public void onResourceReleased(Key cacheKey, Resource resource) { 178 activeResources.remove(cacheKey); 179 if (resource.isCacheable()) { 180 cache.put(cacheKey, resource); 181 } else { 182 resource.recycle(); 183 } 184 } 185 186 private static class ResourceWeakReference extends WeakReference<Resource> { 187 public final Object resource; 188 public final Key key; 189 190 public ResourceWeakReference(Key key, Resource r, ReferenceQueue<? super Resource> q) { 191 super(r, q); 192 this.key = key; 193 resource = r.get(); 194 } 195 } 196 197 private static class RefQueueIdleHandler implements MessageQueue.IdleHandler { 198 private Map<Key, WeakReference<Resource>> activeResources; 199 private ReferenceQueue<Resource> queue; 200 201 public RefQueueIdleHandler(Map<Key, WeakReference<Resource>> activeResources, ReferenceQueue<Resource> queue) { 202 this.activeResources = activeResources; 203 this.queue = queue; 204 } 205 206 @Override 207 public boolean queueIdle() { 208 ResourceWeakReference ref = (ResourceWeakReference) queue.poll(); 209 if (ref != null) { 210 activeResources.remove(ref.key); 211 if (Log.isLoggable(TAG, Log.DEBUG)) { 212 Log.d(TAG, "Maybe leaked a resource: " + ref.resource); 213 } 214 } 215 216 return true; 217 } 218 } 219} 220