Gilde picture request execution process -- into()

Glide's request execution process

  • To understand the request execution process, we first need to know the following questions
    1. When to build the request
    2. When to execute the request
    3. How to build your request
    4. Execute request

1. Request execution timing & 2. Request build timing

  • In the source code flow analysis, we mentioned calling into() to start executing the image request

     public <Y extends Target<TranscodeType>> Y into(Y target) {
            //Omit some codes
         	//1. Get previous requests
            Request previous = target.getRequest();
    		//2. Judge whether the previous request is empty 
            if (previous != null) {
                previous.clear();
                //Remove it from the tracker so that the tracker can track the latest request below
                requestTracker.removeRequest(previous);
                //Internal call REQUEST_POOL.offer(this); Method to put the request into the request pool for reuse
                previous.recycle();
            }
    		// 3. Generate a request according to the target 
            Request request = buildRequest(target);
         	// 4. The setTag method is called internally to give the view TAG to prevent the list images from being disordered
            target.setRequest(request);
         	//5. Add callback to lifecycle (target implements the LifeCycleListener interface)
         	// The lifeCycle here is the lifeCycle in the requestmanager fragment (no interface fragemnt)
            lifecycle.addListener(target);
         	// 6 request the tracker to run request
            requestTracker.runRequest(request);
            return target;
        }
    
  • According to the above code, we can get the answers of 1 and 2 above

    • When calling the into method, first build the request and then execute the request

3. How to build a request

  • According to the above code, when calling buildRequest(target); The request was built when the

        private Request buildRequest(Target<TranscodeType> target) {
            if (priority == null) {
                priority = Priority.NORMAL;
            }
            return buildRequestRecursive(target, null);
        }
    	// 1. You can see that buildrequest recursive (target, null) is called
    	 private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
            //Part of the code determines whether to set the thumbnail and the thumbnail scale
            //Eventually, the obtainRequest method will be executed
            return obtainRequest method(target, sizeMultiplier, priority, parentCoordinator);
        }
    	//2 the method is finally executed to GenericRequest
    public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(//Parameter list){
            GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();
            if (request == null) {
                request = new GenericRequest<A, T, Z, R>();
            }
            request.init(//Parameter list);
            return request;
        }
        // 3 called the init method to generate a request
            private void init(
                LoadProvider<A, T, Z, R> loadProvider,
                A model,
                Key signature,
                Context context,
                Priority priority,
                Target<R> target,
                float sizeMultiplier,
                Drawable placeholderDrawable,
                int placeholderResourceId,
                Drawable errorDrawable,
                int errorResourceId,
                Drawable fallbackDrawable,
                int fallbackResourceId,
                RequestListener<? super A, R> requestListener,
                RequestCoordinator requestCoordinator,
                Engine engine,
                Transformation<Z> transformation,
                Class<R> transcodeClass,
                boolean isMemoryCacheable,
                GlideAnimationFactory<R> animationFactory,
                int overrideWidth,
                int overrideHeight,
                DiskCacheStrategy diskCacheStrategy) {
            this.loadProvider = loadProvider;
            this.model = model;
            this.signature = signature;
            this.fallbackDrawable = fallbackDrawable;
            this.fallbackResourceId = fallbackResourceId;
            this.context = context.getApplicationContext();
            this.priority = priority;
            this.target = target;
            this.sizeMultiplier = sizeMultiplier;
            this.placeholderDrawable = placeholderDrawable;
            this.placeholderResourceId = placeholderResourceId;
            this.errorDrawable = errorDrawable;
            this.errorResourceId = errorResourceId;
            this.requestListener = requestListener;
            this.requestCoordinator = requestCoordinator;
            this.engine = engine;
            this.transformation = transformation;
            this.transcodeClass = transcodeClass;
            this.isMemoryCacheable = isMemoryCacheable;
            this.animationFactory = animationFactory;
            this.overrideWidth = overrideWidth;
            this.overrideHeight = overrideHeight;
            this.diskCacheStrategy = diskCacheStrategy;
            status = Status.PENDING;
           }
          // You can see that in the init method, there are some initialization operations to set attributes, and the request is built
    
    
    • Initialize the request by setting the attribute through the init method of GenericRequest

4. Execution request

  • As we know above, the request is executed through the request tracker

     requestTracker.runRequest(request);
    

What is requestTracker?

  • definition

    • A class used to track, cancel, and restart requests that are in progress, completed, and failed. (request tracker)

    • Two sets are maintained internally

      • // requests is the request queue being executed. requests that are not used are guaranteed to be recycled
        Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>())
         //Queue of pending queued requests
        List<Request> pendingRequests = new ArrayList<Request>();
        

Execution process

  • runRequest
 /**
     * Start tracking request
     */
    public void runRequest(Request request) {
        requests.add(request);
        // Judge whether the current request is suspended. If not, execute begin
        if (!isPaused) {
            request.begin();
        } else {
            // If the request is suspended, join the waiting queue and judge whether it is in the suspended state according to the declaration cycle of the binding. onstart starts to execute onStop to suspend execution
            pendingRequests.add(request);
        }
    }
  • begin method
 public void begin() {
        //1. Judge whether override is called to specify width and height
        status = Status.WAITING_FOR_SIZE;
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            //2. If the width and height are specified, the width and height will be directly used to load the picture focus onSizeReady
            onSizeReady(overrideWidth, overrideHeight);
        } else {
            //3. If the width and height are not specified, measure the width and height according to the drawing results, and finally call onSizeReady 
            //this is the implementation of SizeReadyCallback, onSizeReady
            target.getSize(this);
        }
        if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
            // 4 read the default graph and set it to target
            target.onLoadStarted(getPlaceholderDrawable());
        }
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logV("finished run method in " + LogTime.getElapsedMillis(startTime));
        }
    }
  • getSize method

    • public void getSize(SizeReadyCallback cb) {
                 int currentWidth = getViewWidthOrParam();
                 int currentHeight = getViewHeightOrParam();
          		// If the drawing has been completed and the width and height are valid, onSizeReady is called
                 if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
                     cb.onSizeReady(currentWidth, currentHeight);
                 } else {
                     if (!cbs.contains(cb)) {
                         cbs.add(cb);
                     }
                     // If the drawing is not finished, join the observer
                     if (layoutListener == null) {
                         final ViewTreeObserver observer = view.getViewTreeObserver();
                         layoutListener = new SizeDeterminerLayoutListener(this);
                         observer.addOnPreDrawListener(layoutListener);
                     }
                 }
             }
       
      
    • You can see that onSizeReady is eventually called

      • Set override to specify the direct call of width and height
      • If the width height is not specified, the width height is called according to the measurement result.
  • onSizeReady

    • public void onSizeReady(int width, int height) {
          //Omit some codes
          width = Math.round(sizeMultiplier * width);
          height = Math.round(sizeMultiplier * height);
          //1. Get modeloader
          ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
          //2. Get DataFetcher through modeloader 
          final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
          //3. Get transcoder
          ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
          loadedFromMemoryCache = true;
          // 4. Call engine.load to complete the real picture loading
          loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
                  priority, isMemoryCacheable, diskCacheStrategy, this);
          loadedFromMemoryCache = resource != null;
      	//Omit some codes
      }
      
      • ModelLoader
        • A factory interface is used to convert any complex data model into the original data type (String is passed, and the url corresponds to io input stream), so that the DataFetcher can be used to obtain the data of the resources represented by the model.
      • DataFetcher
        • An interface with multiple implementation classes, all representing loading data from a specified data source, created through ModelLoader
        • For example, if we pass a Sting type url, the corresponding implementation class of DataFetcher is HttpUrlFetcher, which obtains resources from the url, that is, network requests
      • ResourceTranscoder
        • Transcoding resources of one type into resources of another type.
        • For example, BitmapDrawableTranscoder transcodes Bitmap into BitmapDrawable
      • engine.load
        • Where the picture is actually loaded
    • engine.load

      •     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) {        Util.assertMainThread();        long startTime = LogTime.getLogTime();        //Get the unique ID of the ID character transferred to the image. final String id = fetcher.getId(); / / generate the cached key. Rewrite the equals hash algorithm to generate the enginekey key = keyfactory. Buildkey (ID, signature, width, height, loadprovider. Getcachecoder(), loadprovider. Getsourcedecoder(), transformation, loadprovider. Getencoder() , transcoder, loadprovider. Getsourceencoder()); / / get the cache according to the key engineresource <? > cached = loadfromcache (key, ismemorycacheable); / / get the cache at this time. If it is not empty, call ResourceCallback.onResourceReady to recall the cache. If (cached! = null) {CB. onResourceReady (cached) ; if (log.isloggable (tag, log. Verbose)) {logwithtimeandkey ("loaded resource from cache", starttime, key);} return null;} / / get engineresource <? > active = loadfromactiveresources (key, ismemorycacheable) from weak reference cache; / / not null if (active! = null) {/ / call onResourceReady to set the picture CB. onResourceReady (active); if (log. IsLoggable (tag, log. Verbose)) {logwithtimeandkey ("loaded resource from active resources", starttime, key);} return null;} //If not in the cache, start a new thread to create an EngineJob to load EngineJob from the disk or network. 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);        } 		//  If the memory cache and weak reference cache cannot be obtained, start the thread to create an enginerunnable to disk or network load execution logic. In the run method of enginerunnable, 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);        EngineJob.start(runnable) ;        return new LoadStatus(cb, EngineJob);    }	
        
    • Execute the run method down to EngineRunnable

      • public void run() {    // ......    	   resource = decode();   // ......     if (resource == null) {         onLoadFailed(exception);     } else {         onLoadComplete(resource);     } }    	 // decode    	  private Resource<?>  Decode() throws exception {/ / the disk cache defaults to auto. if (isDecodingFromCache()) {/ / read and decode from the disk return decodefromcache();} else {/ / read and decode from the network return decodeFromSource();}}
        
      • When onLoadComplete is successful, it calls onLoadFailed and fails to call the

      • private void onLoadComplete(Resource resource) {    //The manager here is enginejob manager. Onresourceready (resource);}
        
      • Pass the resource by calling onResourceReady

      • @Overridepublic void onResourceReady(final Resource<?> resource) {    this.resource = resource;    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();}
        
      • You can see that a message is sent through the handle

        • Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());
          
        • The above is the Handler creation. You can see that the MainThreadCallback is passed

        • This CallBack is where the returned results are processed

      • private static class MainThreadCallback implements Handler.Callback {        @Override       public boolean handleMessage(Message message) {           if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {               EngineJob job = (EngineJob) message.obj;               if (MSG_COMPLETE == message.what) {                   // Handle main thread task job. Handleresultonmainthread();} Else {/ / handle main thread exception job. Handleexceptiononmainthread();} return true;} return false;       }   }
        
      • Processing main thread tasks

      • private void handleResultOnMainThread() {       //....       engineResource = engineResourceFactory.build(resource, isCacheable);       hasResource = true;     	// If there is a listener reference, it is not put. The number of references + 1 is put into the weak reference cache engineresource. Acquire(); listener.onEngineJobComplete(key, engineResource);         For (resourcecallback CB: CBS) {if (! Isinnoredcallbacks (CB)) {engineresource. Acquire(); / / the callback returns the picture resource CB. Onresourceready (engineresource);}} / / our request has been completed, so we can free up resources. If the number of references is 0, it is removed from the weak reference cache queue and added to the in memory cache engineResource.release();}
        
      • onResourceReady

      • public void onResourceReady(Resource<?> resource) {    onResourceReady(resource, (R) received);}private void onResourceReady(Resource<?> resource, R result) {    //Call onresourceready target.onresourceready (result, animation) of targrt;}// Take imageviewTargt as an example @ overridepublic void onresourceready (Z resource, glideanimation <? Super z > glideanimation) {/ / set image resources. Take BitmapImageViewTarget as an example setresource (resource);} / / bitmapimageviewtargetpublic class BitmapImageViewTarget extends imageviewtarget < bitmap > {public BitmapImageViewTarget (ImageView) {super (view);} protected void setresource (bitmap resource) {/ / set the image resource view.setImageBitmap(resource);}}
        
      • At this point, the picture loading is complete

Execution process summary
  1. Execute the request through the requesttracker and join the set to determine whether it is executing. If it is executing, call begin, otherwise join the waiting set
  2. Call begin to calculate the width and height of the target, and call onSizeReady
  3. Get ModelLoader, datafetcher and resourcetranscoder in onSizeReady, and call engine.load to execute the real loading process
  4. In the engine.load method
    • Get from the memory cache first. If it does not exist, get from the weak reference cache, and set the resource to the target through the onResourceReady callback
    • If neither exists, create an EngineRunnable to obtain it from the disk or network. After obtaining it, convert it to the main thread through the handler, and then set the resource to the target through the onResourceReady callback

notes

  • To sum up, the real image loading execution process is in engine.load
    • This load involves memory cache, disk cache and network request to obtain image data, so it will be mentioned later together with the cache part
    • This article does not cover network request correlation and cache correlation. Please see cache correlation later

Tags: Java Android glide

Posted on Mon, 27 Sep 2021 14:14:40 -0400 by Chris Mayo