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.