Glide lifecycle management

Time:2022-5-4

Glide lifecycle management

1. Glide features
  • Easy to use
  • High configurability and high adaptability
  • Support common image formats (jpg, PNG, GIF, webp)
  • Support multiple data sources (network, local, resources, assets, etc.)
  • Efficient caching strategy (memory and disk image caching are supported, and rgb_565 is adopted as the default bitmap format, with small memory)
  • Lifecycle integration (automatically manage requests according to activity / fragment lifecycle)
  • Efficiently handle bitmaps (reuse bitmaps with bitmappool and actively call recycle to recycle the bitmaps to be recycled)
2. Glide introduction
Glide.with(this)
 //  . Asbitmap() // only static pictures are allowed to be loaded. If GIF is passed in, the first frame will be displayed (before loading)
 //  . Asgif() // specify GIF format (before load)
 //  . Asdrawable() // specify the drawable format (before load)
     . load (imageurl) // url address of the loaded image
     . placeholder (r.drawable. Ic_placeholder) // placeholder image
     . error (r.drawable. Ic_error) // error picture
     . transition (generictransitionoptions. With (r.anim. Zoom_in)) // picture animation
     . override (800800) // set the loading size
     . skipmemorycache (true) // disable memory caching
     . diskcachestrategy (diskcachestrategy. None) // no content is cached
  // . Diskcachestrategy (diskcachestrategy. Data) // only cache the original image
  // . Diskcachestrategy (diskcachestrategy. Resource) // only cache the converted images
  // . Diskcachestrategy (diskcachestrategy. All) // cache all
  // . Diskcachestrategy (diskcachestrategy. Automatic) // glide intelligently selects which caching strategy to use according to image resources (default)
     . listener (New requestlistener < drawable > () {// monitor the picture loading status
        //Picture loading completed
         @Override
         public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
            return false;
         }
         //Picture loading failed
         @Override
         public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
             return false;
         }
     })
    .into(imageView);// Where the picture will eventually be displayed
3. Glide life cycle principle analysis

Glide.with(this)

The with method can accept different types of context, activity, fragmentactivity, fragment and view.

private static volatile Glide glide;
public static Glide get(@NonNull Context context) {
    if (glide == null) {
        synchronized (Glide.class) {
            if (glide == null) {
                checkAndInitializeGlide(context);
            }
        }
    }
    return glide;
}

Dual detection singleton mode (DCL)To ensure the uniqueness of glide object, glide is initialized in the get method, and a glidebuilder object is created through the builder mode (resource request thread pool, local cache loading thread pool, animation thread pool, memory buffer, disk cache tool, etc.).

After constructing the requestmanagerretriever, a requestmanager is returned through get (take activity as an example)

//Get requestmanager through activity
public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      //If it is a sub thread, the application level context is used, that is, life cycle management is not carried out
      return get(activity.getApplicationContext());
    } else {
      //Check whether the activity is destroyed
      assertNotDestroyed(activity)
      //Get the fragmentmanager of the current activity
      android.app.FragmentManager fm = activity.getFragmentManager();
      //Generate a fragment to bind a request management requestmanager
      return fragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

If the current thread is a child thread, there is no need to manage the glide life cycle. Otherwise, create a fragment through the fragmentget function:

private RequestManager fragmentGet(@NonNull Context context,
     @NonNull android.app.FragmentManager fm,
     @Nullable android.app.Fragment parentHint,
     boolean isParentVisible) {
   //① Add a fragment to the current activity to manage the life cycle of the request
   RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
   //Get requestmanager
   RequestManager requestManager = current.getRequestManager();
   //If requestmanager does not exist, create
   if (requestManager == null) {
     Glide glide = Glide.get(context);
     //② Build requestmanager  
     //current. Getglidelifecycle () is the activityfragmentlifecycle, that is, the activityfragmentlifecycle in the fragment will be passed in when building the requestmanager
     requestManager =
         factory.build(
             glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
     //Bind the built requestmanager to the fragment
     current.setRequestManager(requestManager);
   }
   //Returns the manager of the current request
   return requestManager;
 }

① Binding of fragment to activity—>getRequestManagerFragment:

private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    //Get the instantiated fragments through tag (i.e. the same activity glide. With multiple times, there is no need to create multiple fragments)
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      //If there is no fragment for managing the life cycle in the current activity, it is fetched from the cache
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        //If there is no cache, just create a new one
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          //Execute request
          current.getGlideLifecycle().onStart();
        }
        //Add to map cache (prevent duplicate fragment creation)
        pendingRequestManagerFragments.put(fm, current);
        //Bind fragment to activity
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        //Send clean cache after adding
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

② Build requestmanager and set listening

//This factory is to build the requestmanager object
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
    @NonNull
    @Override
    public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
        @NonNull RequestManagerTreeNode requestManagerTreeNode, @NonNull Context context) {
      //Instantiate a requestmanager
      return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
    }
  };
public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> { 

RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      RequestManagerTreeNode treeNode,
      RequestTracker requestTracker,
      ConnectivityMonitorFactory factory,
      Context context) {
    this.glide = glide;
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;
    this.context = context;

    connectivityMonitor =
        factory.build(
            context.getApplicationContext(),
            new RequestManagerConnectivityListener(requestTracker));
        
   //Add lifecycle listener
    if (Util.isOnBackgroundThread()) {
      //The child thread registers the current object with the activityfragmentlifecycle through the handler
      mainHandler.post(addSelfToLifecycle);
    } else {
      //Register the current object with the activityfragmentlifecycle
      lifecycle.addListener(this);
    }
    //Add monitoring for network changes
    lifecycle.addListener(connectivityMonitor);

    defaultRequestListeners =
        new CopyOnWriteArrayList<>(glide.getGlideContext().getDefaultRequestListeners());
    setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());

    glide.registerRequestManager(this);
  }
  //...
    
  //Requestmanager implements fragment lifecycle callback
  @Override
  public synchronized void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }
      
       @Override
  public synchronized void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }
      
      @Override
  public synchronized void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }
      
}

When building the requestmanager, the lifecycle of the requestmanager is associated with the fragment.

Fragment is attached to activity, so the life cycle of activity is contained in fragment. Next, let’s take a look at requestmanagerfragment:

public class RequestManagerFragment extends Fragment {
  //The key to the life cycle lies in the activity fragment lifecycle
  private final ActivityFragmentLifecycle lifecycle;
  public RequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
  }

  RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }
  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

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

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

The key of the life cycle lies in the life cycle. When the life cycle of fragment changes, it will actively notify the life cycle to implement the corresponding methods.

Next, take a look at the activityfragmentlifecycle:

class ActivityFragmentLifecycle implements Lifecycle {
  //All its listeners will be notified when the fragment lifecycle changes
  private final Set<LifecycleListener> lifecycleListeners =
      Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
  private boolean isStarted;
  private boolean isDestroyed;

  @Override
  public void addListener(@NonNull LifecycleListener listener) {
    lifecycleListeners.add(listener);

    if (isDestroyed) {
      listener.onDestroy();
    } else if (isStarted) {
      listener.onStart();
    } else {
      listener.onStop();
    }
  }

  @Override
  public void removeListener(@NonNull LifecycleListener listener) {
    lifecycleListeners.remove(listener);
  }

  void onStart() {
    isStarted = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStart();
    }
  }

  void onStop() {
    isStarted = false;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStop();
    }
  }

  void onDestroy() {
    isDestroyed = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onDestroy();
    }
  }
}

This activity fragment lifecycle holds a lifecycle listener, which will notify all its listeners when the fragment lifecycle changes.

4. Glide life cycle callback process summary
Glide lifecycle management

Glide life cycle png

  Glide. With (this) binds the life cycle of the activity. A new fragment without UI is created in the activity. This fragment holds a lifecycle and notifies the requestmanager of relevant slave operations in the critical lifecycle of the fragment through the lifecycle. Continue loading when the lifecycle is OnStart, pause loading when onstop, and stop loading tasks and clearing operations when ondestory.

Why does glide cache fragments?
//Add to map cache (prevent duplicate fragment creation)
pendingRequestManagerFragments.put(fm, current);
//Bind fragment to activity
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//Send a message to clean up the cache after adding
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();

One scene: load two pictures through glide and set them on two imageviews.

Glide.with(this).load(imageUrl1).into(imageView1);//msg1
Glide.with(this).load(imageUrl2).into(imageView2);//msg2
Glide lifecycle management

Glide cache fragment png

When the getrequestmanagerfragment method is executed, the fragment will be bound to the activity by opening the transaction. The binding operation is finally processed by sending messages through the handler of the main thread, and the messages in the handler are executed in order. If the requestmanagerfragments built by MSG1 are not put into pending requestmanagerfragments, a duplicate fragment will be recreated and added when msg2 is executed. Finally, send a message to clean up the cache (to avoid memory leakage and reduce memory pressure), because the messages are executed in sequence. MSG1 and msg2 have been executed when cleaning up the cache.

How does glide monitor network changes?

When building the requestmanagerlifecycle.addListener(connectivityMonitor);Add monitoring for network changes. Changes in the fragment life cycle will be notified to the corresponding methods in the default implementation class defaultconnectivitymonitor. Register receiver in OnStart and unregisterreceiver in onstop. There is a restart request after network reconnection.

final class DefaultConnectivityMonitor implements ConnectivityMonitor {
  private static final String TAG = "ConnectivityMonitor";
  private final Context context;
  @SuppressWarnings("WeakerAccess") @Synthetic final ConnectivityListener listener;

  @SuppressWarnings("WeakerAccess") @Synthetic boolean isConnected;
  private boolean isRegistered;

  private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(@NonNull Context context, Intent intent) {
      boolean wasConnected = isConnected;
      //Judge network status
      isConnected = isConnected(context);
      if (wasConnected != isConnected) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "connectivity changed, isConnected: " + isConnected);
        }
            
        listener.onConnectivityChanged(isConnected);
      }
    }
  };

  DefaultConnectivityMonitor(@NonNull Context context, @NonNull ConnectivityListener listener) {
    this.context = context.getApplicationContext();
    this.listener = listener;
  }

  private void register() {
    if (isRegistered) {
      return;
    }

    // Initialize isConnected.
    isConnected = isConnected(context);
    try {
      // See #1405
      context.registerReceiver(connectivityReceiver,
          new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
      isRegistered = true;
    } catch (SecurityException e) {
      // See #1417, registering the receiver can throw SecurityException.
      if (Log.isLoggable(TAG, Log.WARN)) {
        Log.w(TAG, "Failed to register", e);
      }
    }
  }

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

    context.unregisterReceiver(connectivityReceiver);
    isRegistered = false;
  }

  @SuppressWarnings("WeakerAccess")
  @Synthetic
  // Permissions are checked in the factory instead.
  @SuppressLint("MissingPermission")
  boolean isConnected(@NonNull Context context) {
    ConnectivityManager connectivityManager =
        Preconditions.checkNotNull(
            (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
    NetworkInfo networkInfo;
    try {
      networkInfo = connectivityManager.getActiveNetworkInfo();
    } catch (RuntimeException e) {
     if (Log.isLoggable(TAG, Log.WARN)) {
        Log.w(TAG, "Failed to determine connectivity status when connectivity changed", e);
      }
      // Default to true;
      return true;
    }
    return networkInfo != null && networkInfo.isConnected();
  }

  @Override
  public void onStart() {
    register();
  }

  @Override
  public void onStop() {
    unregister();
  }

  @Override
  public void onDestroy() {
    // Do nothing.
  }
}

Callback the onconnectivitychanged of the connectivitylistener to process the request

private class RequestManagerConnectivityListener
      implements ConnectivityMonitor.ConnectivityListener {
    @GuardedBy("RequestManager.this")
    private final RequestTracker requestTracker;

    RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {
      this.requestTracker = requestTracker;
    }

    @Override
    public void onConnectivityChanged(boolean isConnected) {
      if (isConnected) {
        synchronized (RequestManager.this) {
          //Restart request after network reconnection
          requestTracker.restartRequests();
        }
      }
    }
  }

Recommended Today

Modify user information changeinfo

When judging the persistence layer: Problem: there is such a problem when modifying user information. For example: the user’s email is not required. It was not empty originally. At this time, the user deletes the mailbox information and submits it. At this time, if it is not empty to judge whether it needs to be […]