Personal blog portal

1. Process diagram

Note:

  • A Fragment/Activity generates a RequestManager
  • Each Application corresponds to an applicationManager, which is a globally unique RequestManager
  • Every RequestManager will have a Lifecycle and a RequestTracker
  • Each RequestTracker has a List< Request >

  1. If it is the main thread, register to create an unbounded Fragment to add to the Fragment/Activity and rely on that Fragment to listen for its life cycle
  2. If it is not the main thread, an application-level Lifecycle is created to simulate the Lifecycle
  3. The Fragment created in 1. Can feed back the memory and interface life cycle, which completes the memory and interface listening
  4. You can use the Fragment to determine whether to monitor the network state (if the interface is missing, there is no need to monitor network state)

Second, source code analysis

Life cycle creation

Glide’s five with methods all end up matching the five get() in the RequestManagerRetrieverThe five gets () in the RequestManagerRetriever end up matching the three methods in the RequestManagerRetriever

  • When the main thread: get (activity. GetApplicationContext ())
  • When the main thread + Activity: fragmentGet (Activity, the Activity getFragmentManager ())
  • When the main thread + fragments: supportFragmentGet (fragments. GetActivity (), fragments. GetChildFragmentManager ())

Let’s start with the simpler, non-mainline case

// RequestManagerRetriever
	public RequestManager get(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else if(Util.isOnMainThread() && ! (contextinstanceof Application)) {
            if (context instanceof FragmentActivity) {
                return get((FragmentActivity) context);
            } else if (context instanceof Activity) {
                return get((Activity) context);
            } else if (context instanceof ContextWrapper) {
                returnget(((ContextWrapper) context).getBaseContext()); }}// Finally get here
        return getApplicationManager(context);
    }

	private RequestManager getApplicationManager(Context context) {
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    applicationManager = new RequestManager(
                    		context.getApplicationContext(),
                            new ApplicationLifecycle(), 
                            newEmptyRequestManagerTreeNode()); }}}// applicationManager is globally unique
        return applicationManager;
    }

// ApplicationLifecycle
	class ApplicationLifecycle implements Lifecycle {
    	@Override
    	public void addListener(LifecycleListener listener) {
    		// A false life cycle is simulated here, only startlistener.onStart(); }}Copy the code

Summary:

  • Create an ApplicationLifecycle (mock/global lifecycle)
  • Into a globally unique applicationManager (RequestManager instance)

Main thread + Activity

// RequestManagerRetriever
	RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
		// This is where the blank Fragment is generatedRequestManagerFragment current = getRequestManagerFragment(fm); . requestManager =new RequestManager(context,
        						// This is where the lifecycle is createdcurrent.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); .return requestManager;
    }

	RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
			
		// a series of blank fragments that verify whether the current interface is bound to Glide
        RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        if (current == null) {
            current = pendingRequestManagerFragments.get(fm);
            if (current == null) {
            	
            	// Create a new one
                current = newRequestManagerFragment(); pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current,FRAGMENT_TAG) .commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); }}return current;
    }

// RequestManagerFragment
	ActivityFragmentLifecycle getLifecycle(a) {
		// It is created in the constructor
        return lifecycle;
    }
	
	public RequestManagerFragment(a) {
		/ / in the end created a ActivityFragmentLifecycle instance, let's see how it works
        this(new ActivityFragmentLifecycle());
    }
	
	RequestManagerFragment(ActivityFragmentLifecycle lifecycle) {
        this.lifecycle = lifecycle;
    }
    
	@Override
    public void onStart(a) {
        super.onStart();
        lifecycle.onStart();
    }

    @Override
    public void onStop(a) {
        super.onStop();
        lifecycle.onStop();
    }

    @Override
    public void onDestroy(a) {
        super.onDestroy();
        lifecycle.onDestroy();
    }

// ActivityFragmentLifecycle
	void onStart(a) {
        isStarted = true;
        for(LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onStart(); }}void onStop(a) {
        isStarted = false;
        for(LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onStop(); }}void onDestroy(a) {
        isDestroyed = true;
        for(LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onDestroy(); }}Copy the code

Very simple,

  • Insert a RequestManagerFragment into the Activity with no interface
  • Create a ActivityFragmentLifecycle instance, in the function is called called RequestManagerFragment life cycle

Main thread + Fragment

We don’t watch it. It’s the same

  • To insert a no interface SupportRequestManagerFragment fragments
  • Create a ActivityFragmentLifecycle instance, in the function is called called SupportRequestManagerFragment life cycle

Memory monitoring

  • In SupportRequestManagerFragment onLowMemory ()
  • OnTrimMemory ()/onLowMemory() in RequestManagerFragment
// SupportRequestManagerFragment
	@Override
    public void onLowMemory(a) {... requestManager.onLowMemory(); }// RequestManagerFragment
	@Override
    public void onTrimMemory(int level) {... requestManager.onTrimMemory(level); }@Override
    public void onLowMemory(a) {... requestManager.onLowMemory(); }// What is requestManager? Remember the code created by the RequestManagerFragment?
// RequestManagerRetriever
	RequestManager fragmentGet(Context context, android.app.FragmentManager fm) { RequestManagerFragment current = getRequestManagerFragment(fm); . requestManager =new RequestManager(context,
        						current.getLifecycle(), 
        						current.getRequestManagerTreeNode());
        // Here here herecurrent.setRequestManager(requestManager); .return requestManager;
    }

// RequestManager
	public void onTrimMemory(int level) {
        glide.trimMemory(level);
    }
    
    public void onLowMemory(a) {
        glide.clearMemory();
    }

/ / Glide singleton
	public void clearMemory(a) {... memoryCache.clearMemory(); bitmapPool.clearMemory(); }public void trimMemory(int level) {... memoryCache.trimMemory(level); bitmapPool.trimMemory(level); }Copy the code

Summary:

  • Blank Fragment returns to Glide when storage becomes tight
  • MemoryCache clears the memoryCache
  • BitmapPool clears the memory cache

MemoryCache is an easy-to-guess Glide used for memory caching; So what is a bitmapPool? Android Glide 3.7.0 source code parsing (four), BitmapPool function and principle

  1. In Android, the image display entity is a Bitmap object. Each image display will first build the image resource into a Bitmap object, and the process of creating and destroying Bitmap consumes system resources, and in serious cases, it will cause frequent GC and interface lag
  2. An example would be a list of avatars, 10 avatars per page, assuming the GC threshold is 10 images

Normal solution: create 10 bitmaps and release them, create another 10 bitmaps to display the next page, so that no slide is triggered a GC Glide solution: Create a BitmapPool according to the thread pool understanding, create 10 bitmaps do not free, the next 10 images, borrow the memory space of the existing bitmap, no matter how many pages slide, will not trigger GC

Request Task Listening

Take a look at the constructor of the RequestManager

// RequestManager
	public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
        this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
    }
    
	RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
            RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
        this.context = context.getApplicationContext();
        this.lifecycle = lifecycle;
        this.treeNode = treeNode;
        this.requestTracker = requestTracker;
        this.glide = Glide.get(context);
        this.optionsApplier = new OptionsApplier();

        ConnectivityMonitor connectivityMonitor = factory.build(context,
                new RequestManagerConnectivityListener(requestTracker));

        if (Util.isOnBackgroundThread()) {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run(a) {
                    lifecycle.addListener(RequestManager.this); }}); }else {
        	// Register the RequestManager itself into lifecycle
            lifecycle.addListener(this);
        }
        // Here is the network state lifecycle registration, which is covered in the next section
        lifecycle.addListener(connectivityMonitor);
    }

// LifecycleListener implementation of RequestManager
	@Override
    public void onStart(a) {
        // onStart might not be called because this object may be created after the fragment/activity's onStart method.
        resumeRequests();
    }

	public void resumeRequests(a) {
        Util.assertMainThread();
        requestTracker.resumeRequests();
    }

    /** * Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE * permission is present) and pauses in progress loads. */
    @Override
    public void onStop(a) {
        pauseRequests();
    }

	public void pauseRequests(a) {
        Util.assertMainThread();
        requestTracker.pauseRequests();
    }
    /** * Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed * requests. */
    @Override
    public void onDestroy(a) {
        requestTracker.clearRequests();
    }
Copy the code
  1. The RequestManager registers itself into Lifecycle at the time of initialization
  2. And by RequestManager LifecycleListener implementation, eventually is called RequestManager. RequestTracker to implement the function

Now let’s track the requestTracker

// RequestManager
	public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
		// This is a new object
        this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
    }

// RequestTracker

	// LifecycleListener.onStart
	public void resumeRequests(a) {
        isPaused = false;
        for (Request request : Util.getSnapshot(requests)) {
            if(! request.isComplete() && ! request.isCancelled() && ! request.isRunning()) { request.begin(); } } pendingRequests.clear(); }// LifecycleListener.onStop
	public void pauseRequests(a) {
        isPaused = true;
        for (Request request : Util.getSnapshot(requests)) {
            if(request.isRunning()) { request.pause(); pendingRequests.add(request); }}}// LifecycleListener.onDestroy
	public void clearRequests(a) {
        for (Request request : Util.getSnapshot(requests)) {
            request.clear();
        }
        pendingRequests.clear();
    }
Copy the code
  1. As you can see, the final call is Request(real) begin(), pause(), clear()
  2. Request(real) is a GenericRequest object (Android Glide 3.7.0).

Keep tracking GenericRequest

// GenericRequest

	// LifecycleListener.onStart
    public void begin(a) {
        startTime = LogTime.getLogTime();
        if (model == null) {
            onException(null);
            return;
        }

        status = Status.WAITING_FOR_SIZE;
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        	// Call this method to start a new download task
            onSizeReady(overrideWidth, overrideHeight);
        } else {
            target.getSize(this);
        }

        if(! isComplete() && ! isFailed() && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); }if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logV("finished run method in "+ LogTime.getElapsedMillis(startTime)); }}// LifecycleListener.onStop
	public void pause(a) {
		// It looks like onStop and onDestroy are the same logic called
        clear();
        status = Status.PAUSED;
    }

	// LifecycleListener.onDestroy
	public void clear(a) {
        Util.assertMainThread();
        if (status == Status.CLEARED) {
            return;
        }
        // Stop tasks are controlled on this side
        cancel();
        // Resource must be released before canNotifyStatusChanged is called.
        if(resource ! =null) {
        	// This is the release of some resources
            releaseResource(resource);
        }
        if (canNotifyStatusChanged()) {
            target.onLoadCleared(getPlaceholderDrawable());
        }
        // Must be after cancel().
        status = Status.CLEARED;
    }

	void cancel(a) {
        status = Status.CANCELLED;
        if(loadStatus ! =null) {
            loadStatus.cancel();
            loadStatus = null; }}// LoadStatus
	public static class LoadStatus {
        private final EngineJob engineJob;
        private final ResourceCallback cb;

        public LoadStatus(ResourceCallback cb, EngineJob engineJob) {
            this.cb = cb;
            this.engineJob = engineJob;
        }

        public void cancel(a) {
        	// the EngineJob is called to cancel the taskengineJob.removeCallback(cb); }}Copy the code
  1. The lifecycle method onStart() finally initiates a Request through begin() of the Request
  2. OnStop () and onDestroy() are implemented using removeCallback() of the EngineJob
  3. This EngineJob manages the scheduling of the Request thread when it is downloaded.

Keep tracking EngineJob to see

// EngineJob
	public void removeCallback(ResourceCallback cb) {
        Util.assertMainThread();
        if (hasResource || hasException) {
            addIgnoredCallback(cb);
        } else {
        	// Remove the listening callback first
            cbs.remove(cb);
            if (cbs.isEmpty()) {
            	// See what it does after it's all removedcancel(); }}}void cancel(a) {
        if (hasException || hasResource || isCancelled) {
            return;
        }
        engineRunnable.cancel();
        Future currentFuture = future;
        if(currentFuture ! =null) {
        	// The future here is an executorService. submit returns the final destination to terminate the task
            currentFuture.cancel(true);
        }
        isCancelled = true;
        listener.onEngineJobCancelled(this, key);
    }
Copy the code

Description:

  1. Glide singleton, when instantiated, creates a diskCacheService (ExecutorService type) and an Engine object, and encapsulates diskCacheService into the Engine
  2. Call engine.load () to perform a Request(real) task
  3. Engine.load() creates an EngineJob instance and passes diskCacheService into it
  4. Enginejob.start () to start a task, which is actually called diskCacheService. Submit ()
  5. So, cancel() above is actually future.cancel ()

6. Each Request(real) corresponds to one EngineJob instance

There’s only one more thing left to watch, the network status change monitor

Monitoring network status changes

Remember the constructor of the RequestManager?

// RequestManager
	
	public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
        this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
    }

    RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
            RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
        this.context = context.getApplicationContext();
        this.lifecycle = lifecycle;
        this.treeNode = treeNode;
        this.requestTracker = requestTracker;
        this.glide = Glide.get(context);
        this.optionsApplier = new OptionsApplier();

        ConnectivityMonitor connectivityMonitor = factory.build(context,
                new RequestManagerConnectivityListener(requestTracker));

        // If we're the application level request manager, we may be created on a background thread. In that case we
        // cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding
        // ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.
        if (Util.isOnBackgroundThread()) {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run(a) {
                    lifecycle.addListener(RequestManager.this); }}); }else {
            lifecycle.addListener(this);
        }
        // This is where a connectivityMonitor instance is created and a listener is registered
        lifecycle.addListener(connectivityMonitor);
    }

/ / DefaultConnectivityMonitor ConnectivityMonitor subclasses
	@Override
    public void onStart(a) {
        register();
    }

    @Override
    public void onStop(a) {
        unregister();
    }
	
	private void register(a) {
        if (isRegistered) {
            return;
        }

        isConnected = isConnected(context);
        context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        isRegistered = true;
    }

    private void unregister(a) {
        if(! isRegistered) {return;
        }

        context.unregisterReceiver(connectivityReceiver);
        isRegistered = false;
    }
Copy the code

Very simple, is the interface alive to monitor the network state change, interface destruction, unregistered to monitor the network state change