Engine.java revision 0be7fb306834626a8c389e0a685d4017f5f84c71
15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)package com.bumptech.glide.load.engine; 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.os.Handler; 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.os.Looper; 55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.os.MessageQueue; 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.util.Log; 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.bumptech.glide.Priority; 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.bumptech.glide.load.Key; 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.bumptech.glide.load.ResourceDecoder; 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.bumptech.glide.load.ResourceEncoder; 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.bumptech.glide.load.Transformation; 125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import com.bumptech.glide.load.data.DataFetcher; 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import com.bumptech.glide.load.engine.cache.DiskCache; 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.bumptech.glide.load.engine.cache.MemoryCache; 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import com.bumptech.glide.request.ResourceCallback; 174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import com.bumptech.glide.util.LogTime; 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.io.InputStream; 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.lang.ref.ReferenceQueue; 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.lang.ref.WeakReference; 222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.HashMap; 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.util.Map; 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.util.concurrent.ExecutorService; 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedListener, Resource.ResourceListener { 2768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) private static final String TAG = "Engine"; 2868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) private final Map<Key, ResourceRunner> runners; 2968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) private final ResourceRunnerFactory factory; 3068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) private final EngineKeyFactory keyFactory; 3168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) private final MemoryCache cache; 3268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) private final Map<Key, WeakReference<Resource>> activeResources; 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private final ReferenceQueue<Resource> resourceReferenceQueue; 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public static class LoadStatus { 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private final EngineJob engineJob; 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private final ResourceCallback cb; 3868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 3968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) public LoadStatus(ResourceCallback cb, EngineJob engineJob) { 404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.cb = cb; 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) this.engineJob = engineJob; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void cancel() { 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) engineJob.removeCallback(cb); 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public Engine(MemoryCache memoryCache, DiskCache diskCache, ExecutorService resizeService, 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ExecutorService diskCacheService) { 514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this(null, memoryCache, diskCache, resizeService, diskCacheService, null, null, null); 524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Engine(ResourceRunnerFactory factory, MemoryCache cache, DiskCache diskCache, ExecutorService resizeService, 554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ExecutorService diskCacheService, Map<Key, ResourceRunner> runners, EngineKeyFactory keyFactory, 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Map<Key, WeakReference<Resource>> activeResources) { 574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.cache = cache; 584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (activeResources == null) { 6068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) activeResources = new HashMap<Key, WeakReference<Resource>>(); 6168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) } 6268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) this.activeResources = activeResources; 6368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 6468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (keyFactory == null) { 6568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) keyFactory = new EngineKeyFactory(); 6668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) } 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) this.keyFactory = keyFactory; 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (runners == null) { 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runners = new HashMap<Key, ResourceRunner>(); 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.runners = runners; 7368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 7468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (factory == null) { 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) factory = new DefaultResourceRunnerFactory(diskCache, new Handler(Looper.getMainLooper()), 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) diskCacheService, resizeService); 7768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) } 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.factory = factory; 7968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) resourceReferenceQueue = new ReferenceQueue<Resource>(); 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MessageQueue queue = Looper.myQueue(); 822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) queue.addIdleHandler(new RefQueueIdleHandler(activeResources, resourceReferenceQueue)); 8368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) cache.setResourceRemovedListener(this); 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param cacheDecoder 884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param fetcher 8968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * @param decoder 9068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * @param encoder 9168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * @param transcoder 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param priority 9368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * @param <T> The type of data the resource will be decoded from. 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param <Z> The type of the resource that will be decoded. 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param <R> The type of the resource that will be transcoded from the decoded resource. 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public <T, Z, R> LoadStatus load(int width, int height, ResourceDecoder<InputStream, Z> cacheDecoder, 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DataFetcher<T> fetcher, ResourceDecoder<T, Z> decoder, Transformation<Z> transformation, 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ResourceEncoder<Z> encoder, ResourceTranscoder<Z, R> transcoder, Priority priority, 1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) boolean isMemoryCacheable, ResourceCallback cb) { 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) long startTime = LogTime.getLogTime(); 1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) final String id = fetcher.getId(); 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) EngineKey key = keyFactory.buildKey(id, width, height, cacheDecoder, decoder, transformation, encoder, 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) transcoder); 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Resource cached = cache.remove(key); 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (cached != null) { 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) cached.acquire(1); 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) activeResources.put(key, new ResourceWeakReference(key, cached, resourceReferenceQueue)); 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) cb.onResourceReady(cached); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (Log.isLoggable(TAG, Log.VERBOSE)) { 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Log.v(TAG, "loaded resource from cache in " + LogTime.getElapsedMillis(startTime)); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return null; 11668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) } 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WeakReference<Resource> activeRef = activeResources.get(key); 11968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (activeRef != null) { 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Resource active = activeRef.get(); 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (active != null) { 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) active.acquire(1); 12368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) cb.onResourceReady(active); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (Log.isLoggable(TAG, Log.VERBOSE)) { 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Log.v(TAG, "loaded resource from active resources in " + LogTime.getElapsedMillis(startTime)); 1264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return null; 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) activeResources.remove(key); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 132 133 ResourceRunner current = runners.get(key); 134 if (current != null) { 135 EngineJob job = current.getJob(); 136 job.addCallback(cb); 137 if (Log.isLoggable(TAG, Log.VERBOSE)) { 138 Log.v(TAG, "added to existing load in " + LogTime.getElapsedMillis(startTime)); 139 } 140 return new LoadStatus(cb, job); 141 } 142 143 long start = LogTime.getLogTime(); 144 ResourceRunner<Z, R> runner = factory.build(key, width, height, cacheDecoder, fetcher, decoder, transformation, 145 encoder, transcoder, priority, isMemoryCacheable, this); 146 runner.getJob().addCallback(cb); 147 runners.put(key, runner); 148 runner.queue(); 149 if (Log.isLoggable(TAG, Log.VERBOSE)) { 150 Log.v(TAG, "queued new load in " + LogTime.getElapsedMillis(start)); 151 Log.v(TAG, "finished load in engine in " + LogTime.getElapsedMillis(startTime)); 152 } 153 return new LoadStatus(cb, runner.getJob()); 154 } 155 156 @Override 157 public void onEngineJobComplete(Key key, Resource resource) { 158 // A null resource indicates that the load failed, usually due to an exception. 159 if (resource != null) { 160 resource.setResourceListener(key, this); 161 activeResources.put(key, new ResourceWeakReference(key, resource, resourceReferenceQueue)); 162 } 163 runners.remove(key); 164 } 165 166 @Override 167 public void onEngineJobCancelled(Key key) { 168 ResourceRunner runner = runners.remove(key); 169 runner.cancel(); 170 } 171 172 @Override 173 public void onResourceRemoved(Resource resource) { 174 resource.recycle(); 175 } 176 177 @Override 178 public void onResourceReleased(Key cacheKey, Resource resource) { 179 activeResources.remove(cacheKey); 180 if (resource.isCacheable()) { 181 cache.put(cacheKey, resource); 182 } else { 183 resource.recycle(); 184 } 185 } 186 187 private static class ResourceWeakReference extends WeakReference<Resource> { 188 public final Object resource; 189 public final Key key; 190 191 public ResourceWeakReference(Key key, Resource r, ReferenceQueue<? super Resource> q) { 192 super(r, q); 193 this.key = key; 194 resource = r.get(); 195 } 196 } 197 198 private static class RefQueueIdleHandler implements MessageQueue.IdleHandler { 199 private Map<Key, WeakReference<Resource>> activeResources; 200 private ReferenceQueue<Resource> queue; 201 202 public RefQueueIdleHandler(Map<Key, WeakReference<Resource>> activeResources, ReferenceQueue<Resource> queue) { 203 this.activeResources = activeResources; 204 this.queue = queue; 205 } 206 207 @Override 208 public boolean queueIdle() { 209 ResourceWeakReference ref = (ResourceWeakReference) queue.poll(); 210 if (ref != null) { 211 activeResources.remove(ref.key); 212 if (Log.isLoggable(TAG, Log.DEBUG)) { 213 Log.d(TAG, "Maybe leaked a resource: " + ref.resource); 214 } 215 } 216 217 return true; 218 } 219 } 220} 221