Glide Source Parsing 03-in
The simplest Glide call, as shown below, will be source code parsing according to Glide's calling order.
Glide.with(this) .load(uri) .into(img);
into method
In the last load method, we initialized some data and returned GenericRequestBuilder or its subclasses. Now, let's look at GenericRequestBuilder. in ()
3.0 GenericRequestBuilder#into()
The current thread will be judged first, and only the main thread can continue to execute.
Also, if no transform() or centerCrop has been set, the scaleType attribute of ImageView prevails.
public Target<TranscodeType> into(ImageView view) { Util.assertMainThread(); //Determine if it's the main thread, if it doesn't throw an exception. //... //If transform() or centerCrop() is not set, then the scaleType attribute of ImageView prevails. if (!isTransformationSet && view.getScaleType() != null) { switch (view.getScaleType()) { case CENTER_CROP: applyCenterCrop(); break; case FIT_CENTER: case FIT_START: case FIT_END: applyFitCenter(); break; //$CASES-OMITTED$ default: // Do nothing. } } return into(glide.buildImageViewTarget(view, transcodeClass)); }
3.1 glide.buildImageViewTarget
Look at Glide.buildImageViewTarget, in which ImageViewTarget Factory# buildTarget () is called.
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) { if (GlideDrawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new GlideDrawableImageViewTarget(view); } else if (Bitmap.class.equals(clazz)) { return (Target<Z>) new BitmapImageViewTarget(view); } else if (Drawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new DrawableImageViewTarget(view); } else { throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)"); } }
This is a factory method that returns the corresponding class based on the clazz parameter currently passed in
- If the asBitmap() method has been called before, the BitmapImageViewTarget object is returned.
- Otherwise, it usually returns the GlideDrawableImageViewTarget object.
- Other situations are usually not needed.
Back to 1.0.0, let's look at the into method again.
3.2 GenericRequestBuilder#into(Y target)
In this method, a Request object is constructed and executed.
public <Y extends Target<TranscodeType>> Y into(Y target) { Util.assertMainThread(); //... Request previous = target.getRequest(); if (previous != null) { previous.clear(); requestTracker.removeRequest(previous); previous.recycle(); } //1.2.1 constructs a Request object Request request = buildRequest(target); target.setRequest(request); lifecycle.addListener(target); //1.2.3 Execute this request requestTracker.runRequest(request); return target; }
3.3 buildRequest
Let's start with buildRequest, which is divided into three cases, but eventually obtainRequest is called
private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) { if (thumbnailRequestBuilder != null) { //Setting up thumbnail (Generic Request Builder) or thumbnail( DrawableRequestBuilder)This will be called here. //... Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator); //... coordinator.setRequests(fullRequest, thumbRequest); return coordinator; } else if (thumbSizeMultiplier != null) { //Setting thumbnail(float) calls here //... Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator); coordinator.setRequests(fullRequest, thumbnailRequest); return coordinator; } else { //If thumbnail (thumbnail) is not set, it will call here. //... return obtainRequest(target, sizeMultiplier, priority, parentCoordinator); } } private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority, RequestCoordinator requestCoordinator) { return GenericRequest.obtain( loadProvider, model, signature, context, priority, target, sizeMultiplier, placeholderDrawable, placeholderId, errorPlaceholder, errorId, fallbackDrawable, fallbackResource, requestListener, requestCoordinator, glide.getEngine(), transformation, transcodeClass, isCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy); }
3.3.2 GenericRequestBuilder#obtainRequest
Let's look at obtainRequest. First, if the request is not initialized, then the new GenericRequest object. The GenericRequest into method is then called, with some assignment code inside, assigning the incoming parameters to the member variables of GenericRequest.
public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(...Many parameters...) { //... if (request == null) { //Create GenericRequest object request = new GenericRequest<A, T, Z, R>(); } //Internally, there are some assignment codes that assign the incoming parameters to the member variables of GenericRequest request.init(loadProvider, model, signature, context, priority, target, sizeMultiplier, placeholderDrawable, placeholderResourceId, errorDrawable, errorResourceId, fallbackDrawable, fallbackResourceId, requestListener, requestCoordinator, engine, transformation, transcodeClass, isMemoryCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy); return request; }
3.4 ReuqestTracker#runRequest
Back in 3.2, let's look at runRequest, which is where it really works. You can see that if you are not in a pause state, request.begein() is called directly. If you are in a pause state, you add it to the queue first, and then call request.begin() when you get back to normal state. That is, request.begin() is called anyway.
public void runRequest(Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); } }
3.5 request#begin()
So what kind of request is it? It's the GenericRequest we analyzed in 1.2.2 GenericRequestBuilder#obtainRequest.
Whether onSizeReady or target.getSize, onSizeReady will eventually be invoked here.
@Override public void begin() { //... if (model == null) { //The model stores the url, where setErrorPlaceholder is invoked and setImageDrawable is finally invoked. onException(null); return; } status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { //The override() API is used to specify a fixed width call here onSizeReady(overrideWidth, overrideHeight); } else { //The override() API is not used to specify a fixed width call here //The target.getSize() method calculates the width of the image based on the layout_width and layout_height values of the ImageView. target.getSize(this); } if (!isComplete() && !isFailed() && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } //... }
3.6 onSizeReady
onSizeReady determines the width of the image and calls engine.load to load it.
public void onSizeReady(int width, int height) { if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { return; } status = Status.RUNNING; width = Math.round(sizeMultiplier * width); height = Math.round(sizeMultiplier * height); //The loadProvider encapsulates Gif Bitmap Wrapper Drawable Transcoder, ImageVideo Model Loader, ImageVideo Gif Drawable LoadProvider, which is built from the load method - > specifically in the Drawable Type Request construction method. //Get Image Video Model Loader ModelLoader<A, T> modelLoader = loadProvider.getModelLoader(); //Get ImageVideoFetcher final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height); if (dataFetcher == null) { onException(new Exception("Failed to load model: \'" + model + "\'")); return; } //Get GifBitmapWrapper Drawable Transcoder ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } loadedFromMemoryCache = true; //[Focus] loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this); loadedFromMemoryCache = resource != null; if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } }
3.7 engine.load
This method is divided into two steps: the first step deals with caching, and the second step actually performs loading.
Build EngineJob, add callback (callback is a GenericRequest object) to EngineJob, and execute the new EngineRunnable
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher, DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) { //Processing Cache final String id = fetcher.getId(); EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(), loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(), transcoder, loadProvider.getSourceEncoder()); EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; } EngineJob current = jobs.get(key); if (current != null) { current.addCallback(cb); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); } EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable); DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation, transcoder, diskCacheProvider, diskCacheStrategy, priority); EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority); jobs.put(key, engineJob); engineJob.addCallback(cb); //True implementation engineJob.start(runnable); //... return new LoadStatus(cb, engineJob); }
3.8 EngineRunnable#run()
engineJob.start() executes the run method of EngineRunnable.
public void run() { if (isCancelled) { return; } Exception exception = null; Resource<?> resource = null; try { //1.3.0 Decoding resource = decode(); } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception decoding", e); } exception = e; } if (isCancelled) { if (resource != null) { resource.recycle(); } return; } //1.3.2 Loading Results if (resource == null) { onLoadFailed(exception); } else { onLoadComplete(resource); } }
3.9 decode
The decode method decodes and returns the Resource object, which will be analyzed in subsequent articles.
3.10 onLoadFailed or onLoadComplete
If resource is null, the logic of loading failure is executed, and if not null, the logic of loading success is executed.