RequestManager.java revision 237ad5590666f641db5762c599be7c7edbfd0645
1package com.bumptech.glide; 2 3import android.content.Context; 4import android.net.Uri; 5import android.os.Handler; 6import android.os.Looper; 7import android.os.ParcelFileDescriptor; 8 9import com.bumptech.glide.load.model.ModelLoader; 10import com.bumptech.glide.load.model.file_descriptor.FileDescriptorModelLoader; 11import com.bumptech.glide.load.model.stream.MediaStoreStreamLoader; 12import com.bumptech.glide.load.model.stream.StreamByteArrayLoader; 13import com.bumptech.glide.load.model.stream.StreamModelLoader; 14import com.bumptech.glide.manager.ConnectivityMonitor; 15import com.bumptech.glide.manager.ConnectivityMonitorFactory; 16import com.bumptech.glide.manager.Lifecycle; 17import com.bumptech.glide.manager.LifecycleListener; 18import com.bumptech.glide.manager.RequestTracker; 19import com.bumptech.glide.util.Util; 20 21import java.io.File; 22import java.io.InputStream; 23import java.net.URL; 24import java.util.UUID; 25 26/** 27 * A class for managing and starting requests for Glide. Can use activity, fragment and connectivity lifecycle events to 28 * intelligently stop, start, and restart requests. Retrieve either by instantiating a new object, or to take advantage 29 * built in Activity and Fragment lifecycle handling, use the static Glide.load methods with your Fragment or Activity. 30 * 31 * @see Glide#with(android.app.Activity) 32 * @see Glide#with(android.support.v4.app.FragmentActivity) 33 * @see Glide#with(android.app.Fragment) 34 * @see Glide#with(android.support.v4.app.Fragment) 35 * @see Glide#with(Context) 36 */ 37public class RequestManager implements LifecycleListener { 38 private final Context context; 39 private Lifecycle lifecycle; 40 private final RequestTracker requestTracker; 41 private final Glide glide; 42 private final OptionsApplier optionsApplier; 43 private DefaultOptions options; 44 45 public RequestManager(Context context, Lifecycle lifecycle) { 46 this(context, lifecycle, new RequestTracker(), new ConnectivityMonitorFactory()); 47 } 48 49 RequestManager(Context context, final Lifecycle lifecycle, RequestTracker requestTracker, 50 ConnectivityMonitorFactory factory) { 51 this.context = context; 52 this.lifecycle = lifecycle; 53 this.requestTracker = requestTracker; 54 this.glide = Glide.get(context); 55 this.optionsApplier = new OptionsApplier(); 56 57 ConnectivityMonitor connectivityMonitor = factory.build(context, 58 new RequestManagerConnectivityListener(requestTracker)); 59 60 // If we're the application level request manager, we may be created on a background thread. In that case we 61 // cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding 62 // ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe. 63 if (Util.isOnBackgroundThread()) { 64 new Handler(Looper.getMainLooper()).post(new Runnable() { 65 @Override 66 public void run() { 67 lifecycle.addListener(RequestManager.this); 68 } 69 }); 70 } else { 71 lifecycle.addListener(this); 72 } 73 lifecycle.addListener(connectivityMonitor); 74 } 75 76 /** 77 * An interface that allows a default set of options to be applied to all requests started from an 78 * {@link com.bumptech.glide.RequestManager}. 79 */ 80 public interface DefaultOptions { 81 /** 82 * Allows the implementor to apply some options to the given request. 83 * 84 * @param model The model that is being loaded. 85 * @param requestBuilder The request builder being used to construct the load. 86 * @param <T> The type of the model. 87 */ 88 public <T> void apply(T model, GenericRequestBuilder<T, ?, ?, ?> requestBuilder); 89 } 90 91 /** 92 * Sets an interface that can apply some default options to all Requests started using this {@link RequestManager}. 93 * 94 * <p> 95 * Note - These options will be retained for the life the of this {@link com.bumptech.glide.RequestManager} 96 * so be wary of using 97 * {@link com.bumptech.glide.GenericRequestBuilder#listener(com.bumptech.glide.request.RequestListener)}} when 98 * starting requests using an {@link android.content.Context} or {@link android.app.Application} to avoid 99 * leaking memory. Any option that does not use an anonymous inner class is generally safe. 100 * </p> 101 * 102 * @param options The default options to apply to all requests. 103 */ 104 public void setDefaultOptions(DefaultOptions options) { 105 this.options = options; 106 } 107 108 /** 109 * Returns true if loads for this {@link RequestManager} are currently paused. 110 * 111 * @see #pauseRequests() 112 * @see #resumeRequests() 113 */ 114 public boolean isPaused() { 115 Util.assertMainThread(); 116 return requestTracker.isPaused(); 117 } 118 119 /** 120 * Cancels any in progress loads, but does not clear resources of completed loads. 121 * 122 * @see #isPaused() 123 * @see #resumeRequests() 124 */ 125 public void pauseRequests() { 126 Util.assertMainThread(); 127 requestTracker.pauseRequests(); 128 } 129 130 /** 131 * Restarts any loads that have not yet completed. 132 * 133 * @see #isPaused() 134 * @see #pauseRequests() 135 */ 136 public void resumeRequests() { 137 Util.assertMainThread(); 138 requestTracker.resumeRequests(); 139 } 140 141 /** 142 * Lifecycle callback that registers for connectivity events (if the android.permission.ACCESS_NETWORK_STATE 143 * permission is present) and restarts failed or paused requests. 144 */ 145 @Override 146 public void onStart() { 147 // onStart might not be called because this object may be created after the fragment/activity's onStart method. 148 resumeRequests(); 149 } 150 151 /** 152 * Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE 153 * permission is present) and pauses in progress loads. 154 */ 155 @Override 156 public void onStop() { 157 pauseRequests(); 158 } 159 160 /** 161 * Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed 162 * requests. 163 */ 164 @Override 165 public void onDestroy() { 166 requestTracker.clearRequests(); 167 } 168 169 /** 170 * Use the given generic model loader to load the given generic data class. 171 * <p> 172 * Warning - This is an experimental api that may change without a change in major version. 173 * </p> 174 * @param modelLoader The {@link ModelLoader} class to use to load the model. 175 * @param dataClass The type of data the {@link ModelLoader} will load. 176 * @param <A> The type of the model to be loaded. 177 * @param <T> The type of the data to be loaded from the mode. 178 * @return A {@link GenericModelRequest} to set options for the load and ultimately the target to load the model 179 * into. 180 */ 181 public <A, T> GenericModelRequest<A, T> using(ModelLoader<A, T> modelLoader, Class<T> dataClass) { 182 return new GenericModelRequest<A, T>(modelLoader, dataClass); 183 } 184 185 /** 186 * Set the {@link ModelLoader} to use for for a new load where the model loader translates from a model to an 187 * {@link InputStream} resource for loading images. 188 * 189 * @param modelLoader The model loader to use. 190 * @param <T> The type of the model. 191 * @return A new {@link ImageModelRequest}. 192 */ 193 public <T> ImageModelRequest<T> using(final StreamModelLoader<T> modelLoader) { 194 return new ImageModelRequest<T>(modelLoader); 195 } 196 197 /** 198 * A convenience method to use a {@link StreamByteArrayLoader} to decode an image from a byte array. 199 * 200 * @param modelLoader The byte array loader. 201 * @return A new {@link ImageModelRequest}. 202 */ 203 public ImageModelRequest<byte[]> using(StreamByteArrayLoader modelLoader) { 204 return new ImageModelRequest<byte[]>(modelLoader); 205 } 206 207 /** 208 * Set the {@link ModelLoader} to use for a new load where the model loader translates from a model to an 209 * {@link ParcelFileDescriptor} resource for loading video thumbnails. 210 * 211 * @param modelLoader The model loader to use. 212 * @param <T> The type of the model. 213 * @return A new {@link VideoModelRequest}. 214 */ 215 public <T> VideoModelRequest<T> using(final FileDescriptorModelLoader<T> modelLoader) { 216 return new VideoModelRequest<T>(modelLoader); 217 } 218 219 /** 220 * Use the {@link com.bumptech.glide.load.model.ModelLoaderFactory} currently registered for {@link String} to load 221 * the image represented by the given {@link String}. Defaults to 222 * {@link com.bumptech.glide.load.model.stream.StreamStringLoader.Factory} and 223 * {@link com.bumptech.glide.load.model.stream.StreamStringLoader} to load the given model. 224 * 225 * @see #using(StreamModelLoader) 226 * 227 * @param string The string representing the image. Must be either a path, or a uri handled by 228 * {@link com.bumptech.glide.load.model.stream.StreamUriLoader} 229 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 230 * into. 231 */ 232 public DrawableTypeRequest<String> load(String string) { 233 return loadGeneric(string); 234 } 235 236 /** 237 * Use the {@link com.bumptech.glide.load.model.ModelLoaderFactory} currently registered for {@link Uri} to load the 238 * image at the given uri. Defaults to {@link com.bumptech.glide.load.model.stream.StreamUriLoader.Factory} and 239 * {@link com.bumptech.glide.load.model.stream.StreamUriLoader}. 240 * 241 * @see #using(StreamModelLoader) 242 * 243 * @param uri The uri representing the image. Must be a uri handled by 244 * {@link com.bumptech.glide.load.model.stream.StreamUriLoader} 245 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 246 * into. 247 */ 248 public DrawableTypeRequest<Uri> load(Uri uri) { 249 return loadGeneric(uri); 250 } 251 252 /** 253 * Use {@link android.provider.MediaStore.Images.Thumbnails} and 254 * {@link android.provider.MediaStore.Video.Thumbnails} to retrieve pre-generated thumbnails for the given uri. 255 * 256 * <p> 257 * Falls back to the registered {@link com.bumptech.glide.load.model.ModelLoaderFactory} registered for 258 * {@link Uri}s if the given uri is not a media store uri or if no pre-generated thumbnail exists for the given 259 * uri. In addition, mixes the given mimeType, dateModified, and orientation into the cache key to detect and 260 * invalidate thumbnails if content is changed locally. 261 * </p> 262 * 263 * @param uri The uri representing the media. 264 * @param mimeType The mime type of the media store media. Ok to default to empty string "". See 265 * {@link android.provider.MediaStore.Images.ImageColumns#MIME_TYPE} or 266 * {@link android.provider.MediaStore.Video.VideoColumns#MIME_TYPE}. 267 * @param dateModified The date modified time of the media store media. Ok to default to 0. See 268 * {@link android.provider.MediaStore.Images.ImageColumns#DATE_MODIFIED} or 269 * {@link android.provider.MediaStore.Video.VideoColumns#DATE_MODIFIED}. 270 * @param orientation The orientation of the media store media. Ok to default to 0. See 271 * {@link android.provider.MediaStore.Images.ImageColumns#ORIENTATION}. 272 * @return A new {@link DrawableRequestBuilder} to set options for the load and ultimately the target to load the 273 * uri into. 274 */ 275 public DrawableTypeRequest<Uri> loadFromMediaStore(Uri uri, String mimeType, long dateModified, int orientation) { 276 ModelLoader<Uri, InputStream> genericStreamLoader = Glide.buildStreamModelLoader(uri, context); 277 ModelLoader<Uri, InputStream> mediaStoreLoader = new MediaStoreStreamLoader(context, genericStreamLoader, 278 mimeType, dateModified, orientation); 279 ModelLoader<Uri, ParcelFileDescriptor> fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(uri, 280 context); 281 return optionsApplier.apply(uri, new DrawableTypeRequest<Uri>(uri, mediaStoreLoader, fileDescriptorModelLoader, 282 context, glide, requestTracker, lifecycle, optionsApplier)); 283 } 284 285 /** 286 * Use the {@link com.bumptech.glide.load.model.ModelLoaderFactory} currently registered for {@link File} to load 287 * the image represented by the given {@link File}. Defaults to 288 * {@link com.bumptech.glide.load.model.stream.StreamFileLoader.Factory} and 289 * {@link com.bumptech.glide.load.model.stream.StreamFileLoader} to load the given model. 290 * 291 * @see #using(StreamModelLoader) 292 * 293 * @param file The File containing the image 294 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 295 * into. 296 */ 297 public DrawableTypeRequest<File> load(File file) { 298 return loadGeneric(file); 299 } 300 301 /** 302 * Use the {@link com.bumptech.glide.load.model.ModelLoaderFactory} currently registered for {@link Integer} to load 303 * the image represented by the given {@link Integer} resource id. Defaults to 304 * {@link com.bumptech.glide.load.model.stream.StreamResourceLoader.Factory} and 305 * {@link com.bumptech.glide.load.model.stream.StreamResourceLoader} to load the given model. 306 * 307 * @see #using(StreamModelLoader) 308 * 309 * @param resourceId the id of the resource containing the image 310 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 311 * into. 312 */ 313 public DrawableTypeRequest<Integer> load(Integer resourceId) { 314 return loadGeneric(resourceId); 315 } 316 317 /** 318 * Use the {@link com.bumptech.glide.load.model.ModelLoaderFactory} currently registered for {@link URL} to load the 319 * image represented by the given {@link URL}. Defaults to 320 * {@link com.bumptech.glide.load.model.stream.HttpUrlGlideUrlLoader} and 321 * {@link com.bumptech.glide.load.data.HttpUrlFetcher} to load the given model. 322 * 323 * @see #using(StreamModelLoader) 324 * 325 * @param url The URL representing the image. 326 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 327 * into. 328 */ 329 public DrawableTypeRequest<URL> load(URL url) { 330 return loadGeneric(url); 331 } 332 333 /** 334 * Use a new {@link StreamByteArrayLoader} to load an image from the given model. 335 * 336 * @see #load(byte[]) 337 * 338 * @param model The data to load. 339 * @param id A unique id that identifies the image represented by the model suitable for use as a cache key 340 * (url, filepath etc). If there is no suitable id, use {@link #load(byte[])} instaed. 341 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the image 342 * into. 343 */ 344 public DrawableTypeRequest<byte[]> load(byte[] model, final String id) { 345 final StreamByteArrayLoader loader = new StreamByteArrayLoader(id); 346 return optionsApplier.apply(model, 347 new DrawableTypeRequest<byte[]>(model, loader, null, context, glide, requestTracker, lifecycle, 348 optionsApplier)); 349 } 350 351 /** 352 * Use a new {@link StreamByteArrayLoader} to load an image from the given model. Suitable when there is no 353 * simple id that represents the given data. 354 * 355 * @param model the data to load. 356 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the image 357 * into. 358 */ 359 public DrawableTypeRequest<byte[]> load(byte[] model) { 360 return load(model, UUID.randomUUID().toString()); 361 } 362 363 /** 364 * Use the {@link com.bumptech.glide.load.model.ModelLoaderFactory}s currently registered for the given model type 365 * for {@link InputStream}s and {@link ParcelFileDescriptor}s to load a thumbnail from either the image or the video 366 * represented by the given model. 367 * 368 * @param model The model the load. 369 * @param <T> The type of the model to load. 370 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the image 371 * into. 372 */ 373 public <T> DrawableTypeRequest<T> load(T model) { 374 return loadGeneric(model); 375 } 376 377 private <T> DrawableTypeRequest<T> loadGeneric(T model) { 378 ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(model, context); 379 ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader = 380 Glide.buildFileDescriptorModelLoader(model, context); 381 if (model != null && streamModelLoader == null && fileDescriptorModelLoader == null) { 382 throw new IllegalArgumentException("Unknown type " + model + ". You must provide a Model of a type for" 383 + " which there is a registered ModelLoader, if you are using a custom model, you must first call" 384 + " Glide#register with a ModelLoaderFactory for your custom model class"); 385 } 386 return optionsApplier.apply(model, new DrawableTypeRequest<T>(model, streamModelLoader, 387 fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier)); 388 } 389 390 /** 391 * A helper class for building requests with custom {@link ModelLoader}s that translate models to 392 * {@link ParcelFileDescriptor} resources for loading video thumbnails. 393 * 394 * @param <T> The type of the model. 395 */ 396 public final class VideoModelRequest<T> { 397 private final ModelLoader<T, ParcelFileDescriptor> loader; 398 399 private VideoModelRequest(ModelLoader<T, ParcelFileDescriptor> loader) { 400 this.loader = loader; 401 } 402 403 public DrawableTypeRequest<T> load(T model) { 404 return optionsApplier.apply(model, new DrawableTypeRequest<T>(model, null, loader, context, glide, 405 requestTracker, lifecycle, optionsApplier)); 406 } 407 } 408 409 /** 410 * A helper class for building requests with custom {@link ModelLoader}s that translate models to 411 * {@link InputStream} resources for loading images. 412 * 413 * @param <T> The type of the model. 414 */ 415 public final class ImageModelRequest<T> { 416 private final ModelLoader<T, InputStream> loader; 417 418 private ImageModelRequest(ModelLoader<T, InputStream> loader) { 419 this.loader = loader; 420 } 421 422 public DrawableTypeRequest<T> load(T model) { 423 return optionsApplier.apply(model, new DrawableTypeRequest<T>(model, loader, null, context, glide, 424 requestTracker, lifecycle, optionsApplier)); 425 } 426 } 427 428 /** 429 * A helper class for building requests with custom {@link ModelLoader}s that requires the user to provide a 430 * specific model. 431 * 432 * @param <A> The type of the model. 433 * @param <T> The type of data the {@link com.bumptech.glide.load.model.ModelLoader} provides an 434 * {@link com.bumptech.glide.load.data.DataFetcher} to convert the model to. 435 */ 436 public final class GenericModelRequest<A, T> { 437 private final ModelLoader<A, T> modelLoader; 438 private final Class<T> dataClass; 439 440 private GenericModelRequest(ModelLoader<A, T> modelLoader, Class<T> dataClass) { 441 this.modelLoader = modelLoader; 442 this.dataClass = dataClass; 443 } 444 445 /** 446 * Sets the specific model that will be loaded. 447 * 448 * @param model The model to use. 449 * @return This request builder. 450 */ 451 public GenericTypeRequest load(A model) { 452 return new GenericTypeRequest(model); 453 } 454 455 /** 456 * A helper class for building requests with custom {@link com.bumptech.glide.load.model.ModelLoader}s that 457 * requires the user to specify a specific resource class that will be loaded. 458 * 459 */ 460 public final class GenericTypeRequest { 461 private final A model; 462 463 private GenericTypeRequest(A model) { 464 this.model = model; 465 } 466 467 /** 468 * Sets the resource class that will be loaded. 469 * 470 * @param resourceClass The class of the resource that will be loaded. 471 * @param <Z> The type of the resource that will be loaded. 472 * @return This request builder. 473 */ 474 public <Z> GenericTranscodeRequest<A, T, Z> as(Class<Z> resourceClass) { 475 return optionsApplier.apply(model, new GenericTranscodeRequest<A, T, Z>(context, glide, model, 476 modelLoader, dataClass, resourceClass, requestTracker, lifecycle, optionsApplier)); 477 } 478 } 479 } 480 481 class OptionsApplier { 482 483 public <A, X extends GenericRequestBuilder<A, ?, ?, ?>> X apply(A model, X builder) { 484 if (options != null) { 485 options.apply(model, builder); 486 } 487 return builder; 488 } 489 490 } 491 492 private static class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener { 493 private RequestTracker requestTracker; 494 495 public RequestManagerConnectivityListener(RequestTracker requestTracker) { 496 this.requestTracker = requestTracker; 497 } 498 499 @Override 500 public void onConnectivityChanged(boolean isConnected) { 501 if (isConnected) { 502 requestTracker.restartRequests(); 503 } 504 } 505 } 506} 507