GenericRequest.java revision 78bad2aa32f824f9e098b5058dfa3506a7ed3f62
1package com.bumptech.glide.request; 2 3import android.content.Context; 4import android.graphics.drawable.Drawable; 5import android.util.Log; 6import com.bumptech.glide.Priority; 7import com.bumptech.glide.load.engine.Resource; 8import com.bumptech.glide.load.ResourceDecoder; 9import com.bumptech.glide.load.ResourceEncoder; 10import com.bumptech.glide.load.Transformation; 11import com.bumptech.glide.load.data.DataFetcher; 12import com.bumptech.glide.load.engine.Engine; 13import com.bumptech.glide.load.model.ModelLoader; 14import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; 15import com.bumptech.glide.provider.LoadProvider; 16import com.bumptech.glide.request.target.Target; 17import com.bumptech.glide.util.LogTime; 18 19import java.io.InputStream; 20import java.util.ArrayDeque; 21import java.util.Queue; 22 23/** 24 * A {@link Request} that loads a {@link Resource} into a given {@link Target}. 25 * 26 * @param <A> The type of the model that the resource will be loaded from. 27 * @param <T> The type of the data that the resource will be loaded from. 28 * @param <Z> The type of the resource that will be loaded. 29 */ 30public class GenericRequest<A, T, Z, R> implements Request, Target.SizeReadyCallback, ResourceCallback { 31 private static final String TAG = "GenericRequest"; 32 33 private int placeholderResourceId; 34 private int errorResourceId; 35 private Context context; 36 private Transformation<Z> transformation; 37 private LoadProvider<A, T, Z, R> loadProvider; 38 private RequestCoordinator requestCoordinator; 39 private A model; 40 private Class<R> transcodeClass; 41 private boolean isMemoryCacheable; 42 private Priority priority; 43 private Target<R> target; 44 private RequestListener<A, R> requestListener; 45 private float sizeMultiplier; 46 private Engine engine; 47 private GlideAnimationFactory<R> animationFactory; 48 private int overrideWidth; 49 private int overrideHeight; 50 private String tag = String.valueOf(hashCode()); 51 52 private Drawable placeholderDrawable; 53 private Drawable errorDrawable; 54 private boolean isCancelled; 55 private boolean isError; 56 private boolean loadedFromMemoryCache; 57 private Resource resource; 58 private Engine.LoadStatus loadStatus; 59 private boolean isRunning; 60 private long startTime; 61 62 private static final Queue<GenericRequest> queue = new ArrayDeque<GenericRequest>(); 63 64 @SuppressWarnings("unchecked") 65 public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain( 66 LoadProvider<A, T, Z, R> loadProvider, 67 A model, 68 Context context, 69 Priority priority, 70 Target<R> target, 71 float sizeMultiplier, 72 Drawable placeholderDrawable, 73 int placeholderResourceId, 74 Drawable errorDrawable, 75 int errorResourceId, 76 RequestListener<A, R> requestListener, 77 RequestCoordinator requestCoordinator, 78 Engine engine, 79 Transformation<Z> transformation, 80 Class<R> transcodeClass, 81 boolean isMemoryCacheable, 82 GlideAnimationFactory<R> animationFactory, 83 int overrideWidth, 84 int overrideHeight) { 85 GenericRequest request = queue.poll(); 86 if (request == null) { 87 request = new GenericRequest(); 88 } 89 request.init(loadProvider, 90 model, 91 context, 92 priority, 93 target, 94 sizeMultiplier, 95 placeholderDrawable, 96 placeholderResourceId, 97 errorDrawable, 98 errorResourceId, 99 requestListener, 100 requestCoordinator, 101 engine, 102 transformation, 103 transcodeClass, 104 isMemoryCacheable, 105 animationFactory, 106 overrideWidth, 107 overrideHeight); 108 return request; 109 } 110 111 private GenericRequest() { 112 113 } 114 115 @Override 116 public void recycle() { 117 loadProvider = null; 118 model = null; 119 context = null; 120 target = null; 121 placeholderDrawable = null; 122 errorDrawable = null; 123 requestListener = null; 124 requestCoordinator = null; 125 engine = null; 126 transformation = null; 127 animationFactory = null; 128 isCancelled = false; 129 isError = false; 130 loadedFromMemoryCache = false; 131 loadStatus = null; 132 isRunning = false; 133 queue.offer(this); 134 } 135 136 137 private void init( 138 LoadProvider<A, T, Z, R> loadProvider, 139 A model, 140 Context context, 141 Priority priority, 142 Target<R> target, 143 float sizeMultiplier, 144 Drawable placeholderDrawable, 145 int placeholderResourceId, 146 Drawable errorDrawable, 147 int errorResourceId, 148 RequestListener<A, R> requestListener, 149 RequestCoordinator requestCoordinator, 150 Engine engine, 151 Transformation<Z> transformation, 152 Class<R> transcodeClass, 153 boolean isMemoryCacheable, 154 GlideAnimationFactory<R> animationFactory, 155 int overrideWidth, 156 int overrideHeight) { 157 this.loadProvider = loadProvider; 158 this.model = model; 159 this.context = context; 160 this.priority = priority; 161 this.target = target; 162 this.sizeMultiplier = sizeMultiplier; 163 this.placeholderDrawable = placeholderDrawable; 164 this.placeholderResourceId = placeholderResourceId; 165 this.errorDrawable = errorDrawable; 166 this.errorResourceId = errorResourceId; 167 this.requestListener = requestListener; 168 this.requestCoordinator = requestCoordinator; 169 this.engine = engine; 170 this.transformation = transformation; 171 this.transcodeClass = transcodeClass; 172 this.isMemoryCacheable = isMemoryCacheable; 173 this.animationFactory = animationFactory; 174 this.overrideWidth = overrideWidth; 175 this.overrideHeight = overrideHeight; 176 177 // We allow null models by just setting an error drawable. Null models will always have empty providers, we 178 // simply skip our sanity checks in that unusual case. 179 if (model != null) { 180 if (loadProvider.getCacheDecoder() == null) { 181 throw new NullPointerException("CacheDecoder must not be null, try .cacheDecoder(ResouceDecoder)"); 182 } 183 if (loadProvider.getSourceDecoder() == null) { 184 throw new NullPointerException("SourceDecoder must not be null, try .imageDecoder(ResourceDecoder) " + 185 "and/or .videoDecoder()"); 186 } 187 if (loadProvider.getEncoder() == null) { 188 throw new NullPointerException("Encoder must not be null, try .encode(ResourceEncoder)"); 189 } 190 if (loadProvider.getTranscoder() == null) { 191 throw new NullPointerException("Transcoder must not be null, try .as(Class, ResourceTranscoder)"); 192 } 193 if (loadProvider.getModelLoader() == null) { 194 throw new NullPointerException("ModelLoader must not be null, try .using(ModelLoader)"); 195 } 196 } 197 } 198 199 @Override 200 public void run() { 201 startTime = LogTime.getLogTime(); 202 if (model == null) { 203 onException(null); 204 return; 205 } 206 207 if (overrideWidth > 0 && overrideHeight > 0) { 208 onSizeReady(overrideWidth, overrideHeight); 209 } else { 210 target.getSize(this); 211 } 212 213 if (!isComplete() && !isFailed()) { 214 setPlaceHolder(); 215 isRunning = true; 216 } 217 if (Log.isLoggable(TAG, Log.VERBOSE)) { 218 logV("finished run method in " + LogTime.getElapsedMillis(startTime)); 219 } 220 } 221 222 public void cancel() { 223 isRunning = false; 224 isCancelled = true; 225 if (loadStatus != null) { 226 loadStatus.cancel(); 227 loadStatus = null; 228 } 229 } 230 231 @Override 232 public void clear() { 233 cancel(); 234 setPlaceHolder(); 235 if (resource != null) { 236 resource.release(); 237 resource = null; 238 } 239 } 240 241 @Override 242 public boolean isRunning() { 243 return isRunning; 244 } 245 246 @Override 247 public boolean isComplete() { 248 return resource != null; 249 } 250 251 @Override 252 public boolean isFailed() { 253 return isError; 254 } 255 256 private void setPlaceHolder() { 257 if (!canSetPlaceholder()) return; 258 259 target.setPlaceholder(getPlaceholderDrawable()); 260 } 261 262 private void setErrorPlaceholder() { 263 if (!canSetPlaceholder()) return; 264 265 Drawable error = getErrorDrawable(); 266 if (error != null) { 267 target.setPlaceholder(error); 268 } else { 269 setPlaceHolder(); 270 } 271 } 272 273 private Drawable getErrorDrawable() { 274 if (errorDrawable == null && errorResourceId > 0) { 275 errorDrawable = context.getResources().getDrawable(errorResourceId); 276 } 277 return errorDrawable; 278 } 279 280 private Drawable getPlaceholderDrawable() { 281 if (placeholderDrawable == null && placeholderResourceId > 0) { 282 placeholderDrawable = context.getResources().getDrawable(placeholderResourceId); 283 } 284 return placeholderDrawable; 285 } 286 287 @Override 288 public void onSizeReady(int width, int height) { 289 if (Log.isLoggable(TAG, Log.VERBOSE)) { 290 logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); 291 } 292 if (isCancelled) { 293 return; 294 } 295 296 width = Math.round(sizeMultiplier * width); 297 height = Math.round(sizeMultiplier * height); 298 ResourceDecoder<InputStream, Z> cacheDecoder = loadProvider.getCacheDecoder(); 299 ResourceDecoder<T, Z> decoder = loadProvider.getSourceDecoder(); 300 ResourceEncoder <Z> encoder = loadProvider.getEncoder(); 301 ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder(); 302 ModelLoader<A, T> modelLoader = loadProvider.getModelLoader(); 303 304 final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height); 305 306 if (Log.isLoggable(TAG, Log.VERBOSE)) { 307 logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); 308 } 309 loadedFromMemoryCache = true; 310 loadStatus = engine.load(width, height, cacheDecoder, dataFetcher, decoder, transformation, 311 encoder, transcoder, priority, isMemoryCacheable, this); 312 loadedFromMemoryCache = resource != null; 313 if (Log.isLoggable(TAG, Log.VERBOSE)) { 314 logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); 315 } 316 } 317 318 private boolean canSetImage() { 319 return requestCoordinator == null || requestCoordinator.canSetImage(this); 320 } 321 322 private boolean canSetPlaceholder() { 323 return requestCoordinator == null || requestCoordinator.canSetPlaceholder(this); 324 } 325 326 private boolean isFirstImage() { 327 return requestCoordinator == null || !requestCoordinator.isAnyRequestComplete(); 328 } 329 330 @SuppressWarnings("unchecked") 331 @Override 332 public void onResourceReady(Resource resource) { 333 isRunning = false; 334 if (!canSetImage()) { 335 resource.release(); 336 return; 337 } 338 if (resource == null || !transcodeClass.isAssignableFrom(resource.get().getClass())) { 339 if (resource != null) { 340 resource.release(); 341 } 342 onException(new Exception("Expected to receive an object of " + transcodeClass + " but instead got " + 343 (resource != null ? resource.get() : null))); 344 return; 345 } 346 R result = (R) resource.get(); 347 348 if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache, 349 isFirstImage())) { 350 GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstImage()); 351 target.onResourceReady(result, animation); 352 } 353 354 this.resource = resource; 355 356 if (Log.isLoggable(TAG, Log.VERBOSE)) { 357 logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: " 358 + (resource.getSize() / (1024d * 1024d)) + " fromCache: " + loadedFromMemoryCache); 359 } 360 } 361 362 @Override 363 public void onException(Exception e) { 364 if (Log.isLoggable(TAG, Log.DEBUG)) { 365 Log.d(TAG, "load failed", e); 366 } 367 368 isRunning = false; 369 isError = true; 370 //TODO: what if this is a thumbnail request? 371 if (requestListener == null || !requestListener.onException(e, model, target, isFirstImage())) { 372 setErrorPlaceholder(); 373 } 374 } 375 376 private void logV(String message) { 377 Log.v(TAG, message + " this: " + tag); 378 } 379} 380