Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

preface

Glide Picture loading framework has become an integral part of most apps when we write Android applications. I am prepared to be divided into the top, middle and next three articles to interpret Glide source code. Next, I will interpret the upper part of Glide source code from the following points.

  • Glide Network request
  • Glide life cycle example
  • Glide life cycle management
  • Why can Glide listen to network judgment

1. Glide Network request

Glide before talking about network request, first look at the most primitive network image request loading way.

   public void loadImageUrl(View view) {
        // The most primitive network image loading
        HttpURLConnection; // HttpURLConnection
        //2. Render UI
        // switch to main thread
        // Change the flow to Bitmap
        // Set the bitmap to imageView
        final String url = "Https://img0.baidu.com/it/u=3736037748, 233424948 & FM = 26 & FMT = auto&gp = 0. JPG";

        // Network requests for non-mainline operations
        new Thread(new Runnable() {
            @Override
            public void run(a) {
              final Bitmap bitmap =  getImageBitmap(url);
               runOnUiThread(new Runnable() {
                   @Override
                   public void run(a) { iv_image1.setImageBitmap(bitmap); }}); } }).start(); }//http
    private Bitmap  getImageBitmap(String url){
        Bitmap bitmap=null;
        try {
            URL imageUrl = new URL(url);
            // HttpURLConnection is required
            HttpURLConnection  conn = (HttpURLConnection) imageUrl.openConnection();
            conn.connect();
            InputStream is = conn.getInputStream();
            // The Bitmap factory class is converted to Bitmap
            bitmap = BitmapFactory.decodeStream(is);
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
Copy the code

Very primitive: start a child thread, get the connection flow via HttpURLConnection, and finally convert it to a Bitmap via BitmapFactory.

So let’s see how Glide does network requests.

Positioning to HttpUrlFetcher. The class

public class HttpUrlFetcher implements DataFetcher<InputStream> {... slightly@Override
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0.null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Failed to load data for url", e);
      }
      callback.onLoadFailed(e);
    } finally {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished http url fetcher fetch in "+ LogTime.getElapsedMillis(startTime)); }}}... Slightly}Copy the code

The source code parsing

Obviously this calls the loadDataWithRedirects method to get the corresponding InputStream.

loadDataWithRedirects

private InputStream loadDataWithRedirects(URL url, intredirects, URL lastUrl, ... A urlConnection = connectionFactory. Build (url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
      urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(timeout);
    urlConnection.setReadTimeout(timeout);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true);
    urlConnection.setInstanceFollowRedirects(false);
    urlConnection.connect();
    stream = urlConnection.getInputStream();
    if (isCancelled) {
      return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {
      return getStreamForSuccessfulRequest(urlConnection);
    } else if (isHttpRedirect(statusCode)) {
      String redirectUrlString = urlConnection.getHeaderField("Location");
      if (TextUtils.isEmpty(redirectUrlString)) {
        throw new HttpException("Received empty or null redirect url");
      }
      URL redirectUrl = new URL(url, redirectUrlString);
      cleanup();
      return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
    } else if (statusCode == INVALID_STATUS_CODE) {
      throw new HttpException(statusCode);
    } else {
      throw newHttpException(urlConnection.getResponseMessage(), statusCode); }}Copy the code

The source code parsing

This code shows that Glide framework is still connected to the network through HttpURLConnection.

2, Glide life cycle example

Before formally explaining Glide’s life cycle, let’s take the familiar relationship between Activity and Fragment as an example.

MainActivity

public class MainActivity extends Activity {

    private static final String TAG = "1111--MainActivity";
    ImageView iv_image1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"MainActivity--onCreate");
        setContentView(R.layout.activity_main);
        iv_image1 = findViewById(R.id.iv_image1);
        FragmentManager fragmentManager = getFragmentManager();
        // Start something
        FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
        beginTransaction.replace(android.R.id.content,new Fragment1());
        beginTransaction.commit();
    }

    @Override
    protected void onStart(a) {
        super.onStart();
        Log.d(TAG,"MainActivity--onStart");
    }

    @Override
    protected void onRestart(a) {
        super.onRestart();
        Log.d(TAG,"MainActivity--onRestart");
    }

    @Override
    protected void onResume(a) {
        super.onResume();
        Log.d(TAG,"MainActivity--onResume");
    }

    @Override
    protected void onPause(a) {
        super.onPause();
        Log.d(TAG,"MainActivity--onPause");
    }

    @Override
    protected void onStop(a) {
        super.onStop();
        Log.d(TAG,"MainActivity--onStop");
    }

    @Override
    protected void onDestroy(a) {
        super.onDestroy();
        Log.d(TAG,"MainActivity--onDestroy"); }}Copy the code

Fragment

public class Fragment1 extends Fragment {
    private static String TAG = "1111--Fragment1";
    // Its function is just to give us a life cycle of listening

    public void onAttach(Context context) {
        super.onAttach(context);
        Log.d(TAG,"Fragment1--onAttach");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"Fragment1--onCreate");
    }

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG,"Fragment1--onCreateView");
        View root = inflater.inflate(R.layout.fragment, container, false);
        return root;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d(TAG,"Fragment1--onActivityCreated");
    }

    @Override
    public void onStart(a) {
        super.onStart();
        Log.d(TAG,"Fragment1--onStart");
    }

    @Override
    public void onResume(a) {
        super.onResume();
        Log.d(TAG,"Fragment1--onResume");
    }

    @Override
    public void onPause(a) {
        super.onPause();
        Log.d(TAG,"Fragment1--onPause");
    }

    @Override
    public void onStop(a) {
        super.onStop();
        Log.d(TAG,"Fragment1--onStop");
    }

    @Override
    public void onDestroyView(a) {
        super.onDestroyView();
        Log.d(TAG,"Fragment1--onDestroyView");
    }

    @Override
    public void onDestroy(a) {
        super.onDestroy();
        Log.d(TAG,"Fragment1--onDestroy");
    }

    @Override
    public void onDetach(a) {
        super.onDetach();
        Log.d(TAG,"Fragment1--onDetach"); }}Copy the code

This is nothing to say, just run it and see what happens.

MainActivity: MainActivity--onCreate
Fragment1: Fragment1--onAttach
Fragment1: Fragment1--onCreate
Fragment1: Fragment1--onCreateView
Fragment1: Fragment1--onActivityCreated
MainActivity: MainActivity--onStart
Fragment1: Fragment1--onStart
MainActivity: MainActivity--onResume
Fragment1: Fragment1--onResume
Fragment1: Fragment1--onPause
MainActivity: MainActivity--onPause
Fragment1: Fragment1--onStop
MainActivity: MainActivity--onStop
Fragment1: Fragment1--onDestroyView
Fragment1: Fragment1--onDestroy
Fragment1: Fragment1--onDetach
MainActivity: MainActivity--onDestroy
Copy the code

So this whole series of life cycles, and I’ll summarize it in one last picture.

Glide life cycle management

It is a Fragment of an Activity. It is a Fragment of an Activity. It is a Fragment of an Activity.

Glide.with(this).load(url).into(iv_image1);
Copy the code

This short sentence, completed the entire network picture loading. Let’s go first with.

  @NonNull
  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
Copy the code

In the get.

  @NonNull
  public RequestManager get(@NonNull Activity activity) {
    // Whether the current image loading page is running in the background
    if (Util.isOnBackgroundThread()) {
      // If the page corresponding to the loaded image is running in the background, then enter this logic
      return get(activity.getApplicationContext());
    } else {
      // Determine whether the currently displayed activity is destroyed.
      assertNotDestroyed(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); }}Copy the code

There is a logical determination that indicates whether the currently loaded image is displayed in the background page. Now we respectively in the background, not the background of the two directions to interpret the source.

3.1. The current picture loading page is running in the background

Then the corresponding get method will be called directly

  @NonNull
  public RequestManager get(@NonNull 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()); }}return getApplicationManager(context);
  }
Copy the code

The source code interpretation

Because of the above arguments for: activity. GetApplicationContext (), so it is impossible to enter the if condition judgment, will directly into the getApplicationManager method. Check it out.

 @NonNull
  private RequestManager getApplicationManager(@NonNull Context context) {
    // Either an application context or we're on a background thread.
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  newEmptyRequestManagerTreeNode(), context.getApplicationContext()); }}}return applicationManager;
  }
Copy the code

The source code parsing

Take a close look at the source code and use ApplicationLifecycle. That is, when the interface for the image we load is running in the background, the corresponding lifecycle is bound to the ApplicationLifecycle.

Now it is time to analyze the source code of the image load running in the foreground.

3.2 The current picture loading page is running in the foreground

  @NonNull
  public RequestManager get(@NonNull Activity activity) {
    // Whether the current image loading page is running in the background
    if (Util.isOnBackgroundThread()) {
      // If the page corresponding to the loaded image is running in the background, then enter this logic
      return get(activity.getApplicationContext());
    } else {
      // Determine whether the currently displayed activity is destroyed.
      assertNotDestroyed(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); }}Copy the code

The source code parsing

Continuing here, we’ve just analyzed the source code in the if logic, now we need to analyze the else logic. AssertNotDestroyed is called first to determine whether the activity is destroyed. The second core logic is in the fragmentGet method, so go in there.

  @NonNull
  private RequestManager fragmentGet(@NonNull Context context,
      @NonNull android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
Copy the code

The source code parsing

Here seemed to see a faint fragments, through getRequestManagerFragment method is obtained. Continue to see getRequestManagerFragment method.

  @NonNull
  private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
    // If the first layer is empty, fetch the corresponding Fragment from the cache
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
      	// Create a new Fragment if the cache is still empty
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        // Add the Fragment to the cachependingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); }}return current;
  }
Copy the code

The source code parsing

Here you can see, this is getRequestManagerFragment method returns the fragments, and the currently displayed activity bound to each other, but still Glide with fragments of life cycle? Enter the RequestManagerFragment with this query.

RequestManagerFragment.class

public class RequestManagerFragment extends Fragment {
  private static final String TAG = "RMFragment";
  private finalActivityFragmentLifecycle lifecycle; . slightly@VisibleForTesting
  @SuppressLint("ValidFragment")
  RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle; }... slightly@NonNull
  ActivityFragmentLifecycle getGlideLifecycle(a) {
    returnlifecycle; }... slightly@Override
  public void onDetach(a) {
    super.onDetach();
    unregisterFragmentWithRoot();
  }

  @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();
    unregisterFragmentWithRoot();
  }

  @Override
  public String toString(a) {
    return super.toString() + "{parent=" + getParentFragmentUsingHint() + "}"; }... Slightly}Copy the code

The source code parsing

Glide loaded images that display activities currently will create or retrieve fragments that have already been created for the corresponding empty page. Then will correspond to the inside of the Fragment lifecycle onStart, onStop, onDestroy these three methods by Glide with ActivityFragmentLifecycle binding of the life cycle. That is, when the Fragment performs these three life-cycle methods, the corresponding Glide handles the life-cycle logic.

Now let’s use the example onDestroy to verify that this is true.

  @Override
  public void onDestroy(a) {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
  }
Copy the code

When the onDestroy method is executed, onDestroy will also be executed lifecycle. Now go into onDestroy for Lifecycle and have a look.

As shown in the figure

Enter the ActivityFragmentLifecycle RequestManager implementation class

public class RequestManager implements LifecycleListener.ModelTypes<RequestBuilder<Drawable>> {... slightly@Override
  public void onStart(a) {
    resumeRequests();
    targetTracker.onStart();
  }

  @Override
  public void onStop(a) {
    pauseRequests();
    targetTracker.onStop();
  }

  @Override
  public void onDestroy(a) {
    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); }... Slightly}Copy the code

The source code parsing

This is a complete confirmation that when an activity executes its lifecycle, the Fragment that is connected to it will execute its lifecycle, and that the lifecycle within it will execute its logic.

4. Why can Glide monitor network judgment

When we use Glide switching network (WIFI/ traffic switching), Glide can still load network pictures normally. What does it do in there? We went in with that question.

public class Glide implements ComponentCallbacks2 {... slightly@NonNull
  public static RequestManager with(@NonNull Activity activity) {
    returngetRetriever(activity).get(activity); }... Slightly}Copy the code

The source code parsing

Here the with method returns the RequestManager, and the core logic is right there. Go inside.

public class RequestManager implements LifecycleListener.ModelTypes<RequestBuilder<Drawable>> {... Lifecycle Manager(Lifecycle Manager) treeNode (Lifecycle Manager) 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));

    if (Util.isOnBackgroundThread()) {
      mainHandler.post(addSelfToLifecycle);
    } else {
      lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);

    setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());

    glide.registerRequestManager(this); }... slightlyprivate static class RequestManagerConnectivityListener implements ConnectivityMonitor
      .ConnectivityListener {
    private final RequestTracker requestTracker;

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

    @Override
    public void onConnectivityChanged(boolean isConnected) {
      if(isConnected) { requestTracker.restartRequests(); }}}}Copy the code

The source code parsing

Can be seen from this code, inside the constructor registered RequestManagerConnectivityListener events, And this RequestManagerConnectivityListener class implements the ConnectivityMonitor ConnectivityMonitor interface. Check it out.

As is shown in

Enter the DefaultConnectivityMonitor look inside.

final class DefaultConnectivityMonitor implements ConnectivityMonitor {... slightlyprivate final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(@NonNull Context context, Intent intent) {
      boolean wasConnected = isConnected;
      isConnected = isConnected(context);
      if(wasConnected ! = isConnected) {if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "connectivity changed, isConnected: "+ isConnected); } listener.onConnectivityChanged(isConnected); }}}; . Slightly}Copy the code

The source code parsing

Here the core logic defines a broadcast in which the isConnected method is called. Follow up.

  @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);
      }
      return true;
    }
    returnnetworkInfo ! =null && networkInfo.isConnected();
  }
Copy the code

The source code parsing

Context.CONNECTIVITY_SERVICE has registered the broadcast of the network connection. When there is a normal network connection, the broadcast will be retrieved in real time. Let’s go back to the previous step.

final class DefaultConnectivityMonitor implements ConnectivityMonitor {... slightlyprivate final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(@NonNull Context context, Intent intent) {
      boolean wasConnected = isConnected;
      isConnected = isConnected(context);
      if(wasConnected ! = isConnected) {if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "connectivity changed, isConnected: "+ isConnected); } listener.onConnectivityChanged(isConnected); }}}; . Slightly}Copy the code

The source code parsing

Here if (wasConnected! = isConnected), that is, when the network changes, the onConnectivityChanged method below is called. Keep going.

public class RequestManager implements LifecycleListener.ModelTypes<RequestBuilder<Drawable>> {... slightlyprivate static class RequestManagerConnectivityListener implements ConnectivityMonitor
      .ConnectivityListener {... slightly@Override
    public void onConnectivityChanged(boolean isConnected) {
      if(isConnected) { requestTracker.restartRequests(); }}}... Slightly}Copy the code

The source code parsing

If the network state is available, the restartRequests method is called. I’m going to go to.

  public void restartRequests(a) {
    for (Request request : Util.getSnapshot(requests)) {
      if(! request.isComplete() && ! request.isCleared()) { request.clear();if(! isPaused) { request.begin(); }else {
          // Ensure the request will be restarted in onResume.pendingRequests.add(request); }}}}Copy the code

The source code parsing

This will traverse all the network image loading, first to determine whether a single image is loaded, delete the corresponding image loading, second to determine whether to pause during the loading process, if it is paused, then restart, and finally if the loading has not started, if it is not loaded, add to the queue.

5, summary

This article, mainly explains Glide network request, life cycle, and the corresponding network monitoring processing.

In the next chapter, the main interpretation of Glide with, load, into trilogy.