Glide核心设计一:皮皮虾,我们走
原文鏈接:Glide核心設計一:皮皮蝦,我們走
引言
皮皮蝦,又名蝦姑,是淡水中的強者。其頭部的兩個錘節,可以輕易破壞貝類的外殼,身體上的步足可以保證快速移動。這些優秀的品質,使它被表情包盯上。
Glide,作為Android最優秀的圖片加載框架之一,能和Activity和Fragment的生命周期綁定,是區別于其它網絡框架的核心特征,也是本文分析的重點。
我們將此特征和皮皮蝦表情包做一個類比:
圖片網絡請求緊跟Activity、Fragment的生命周期,當頁面不可交互時自動停止加載,當回到可交互狀態時,繼續加載。就像表情包(Activity、Fragment)控制皮皮蝦(圖片請求)一樣。
框架設計
簡單使用
Glide.with(Context).load(String).into(ImageView)可實現從網絡中獲取圖片并展示到ImageView當中。其中和頁面作生命周期綁定的主要入口是Glide.with(Context)。按照一般的分析邏輯應該先分析源碼,才得出結論,但因一入源碼深似海,不利于整體把握,所以先給出結論。Glide.with(Context)返回的是一個RequestManager,我們來看RequestManager的類的說明注釋。
A class for managing and starting requests for Glide. Can use activity, fragment and connectivity lifecycle events to intelligently stop, start, and restart requests. Retrieve either by instantiating a new object, or to take advantage built in Activity and Fragment lifecycle handling, use the static Glide.load methods with your Fragment or Activity.
由此可知,該類就是用于將請求和Activity或Framgent的生命周期做綁定。
類圖
將和生命周期相關的類圖如下(省略大部分類的變量和方法):
類的簡單介紹
類的聯系
以上對各類有一個簡單的了解,接下來將重點講清楚各類之間的聯系。整個生命周期的綁定分為四部分。
代碼解讀
根據以上內容可直接跟代碼可跳過以下內容,印象更加深刻。
第一部分:Glide.with(Context)
public static RequestManager with(Context context) {RequestManagerRetriever retriever = RequestManagerRetriever.get();return retriever.get(context);} `復制代碼調用RequestManagerRetriever的get方法如下:
public RequestManager get(Context context) {if (context == null) {throw new IllegalArgumentException("You cannot start a load on a null Context");} else if (Util.isOnMainThread() && !(context instanceof Application)) {if (context instanceof FragmentActivity) { //傳入的是Fragmentreturn get((FragmentActivity) context);} else if (context instanceof Activity) { //傳入的是Acitivityreturn get((Activity) context);} else if (context instanceof ContextWrapper) { //傳入的是Applicationreturn get(((ContextWrapper) context).getBaseContext());}}return getApplicationManager(context);}復制代碼以傳入參數為Activity類型為例,代碼如下:
public RequestManager get(Activity activity) {if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);android.app.FragmentManager fm = activity.getFragmentManager(); //獲取FragmentManagerreturn fragmentGet(activity, fm); }}復制代碼主要調用fragmentGet方法,代碼如下:
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {RequestManagerFragment current = getRequestManagerFragment(fm); RequestManager requestManager = current.getRequestManager(); //根據RequestManagerFragment獲取RequestManager,一個RequestManagerFragment包含一個RequestManagerif (requestManager == null) { //若RequestManager為空,則新建一個并且設置到RequestManagerFragment中requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());current.setRequestManager(requestManager);}return requestManager;}復制代碼getRequestManagerFragment(fm)函數主要是根據FragmentManager獲取Fragment,代碼如下:
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); //通過Tag查找if (current == null) { //若為空,從緩存pendingRequestManagerFragments中查找current = pendingRequestManagerFragments.get(fm);if (current == null) { //緩存中也不存在,則新建一個RequestManagerFragment,并且添加到頁面中。current = new RequestManagerFragment();pendingRequestManagerFragments.put(fm, current);fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();}}return current;}復制代碼以上就是根據傳入的Context類型創建RequestManager的代碼部分。
第二部分:監聽不可見Fragment的生命周期并傳遞給RequestManager
添加不可見Fragment的目的,就是因為該Fragment和父類的Activity具有同樣的生命周期,無須改動原有Activity的代碼,即可實現生命周期的監聽。
RequestManagerFragment生命周期相關的代碼如下:
可以看出,Fragment的聲明周期的監聽都轉移到類型是ActivityFragmentLifecycle的變量lifecycle中的對應方法執行。查看ActivityFragmentLifecycle的代碼:
void onStart() {isStarted = true;//循環set集合lifecycleListeners中所有LifecycleListener,執行對應的onStartfor (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {lifecycleListener.onStart();}}//省略onStop()和onDestroy()方法,和onStart()方法類似。復制代碼set集合的LifecycleListener是如何添加進去的,看ActivityFragmentLifecycle中的代碼:
public void addListener(LifecycleListener listener) {lifecycleListeners.add(listener);if (isDestroyed) {//如果當前頁面已經被destroy,則調用對應的onDestroylistener.onDestroy();} else if (isStarted) {//如果當前頁面已經開啟,則調用對應的onStartlistener.onStart();} else { //其他情況調用onStop方法listener.onStop();}}復制代碼addListener(LifecycleListener listener)方法是接口Lifecycle的方法。RequestManagerFragment提供一個公有方法:
ActivityFragmentLifecycle getLifecycle() {return lifecycle;}復制代碼回看第一部分創建RequestManager時:
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());復制代碼RequestManagerFragment中的Lifecycle作為RequestManager的構造函數的參數傳遞給RequestManager。RequestManager構造函數如下:
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() {public void run() {//在主線程中將當前類實現的LifecycleListener添加到lifecycle中。lifecycle.addListener(RequestManager.this);}});} else {//在主線程中將當前類實現的LifecycleListener添加到lifecycle中。lifecycle.addListener(this);}lifecycle.addListener(connectivityMonitor);}復制代碼由此可見,lifecycle添加的就是RequestManager實現的LifecycleListener接口。
第三部分:RequestManager實現LifecycleListener
接著查看RequestManager實現LifecycleListener的方法:
public void onStart() {// onStart might not be called because this object may be created after the fragment/activity's onStart method.resumeRequests();}public void onStop() {pauseRequests();}public void onDestroy() {requestTracker.clearRequests();}復制代碼繼續進入resumeRequests()、pauseRequests()和requestTracker.clearRequests()方法可知,都是調用RequestTracker相應的方法,RequestTracker類包含一個集合的Request,該集合包含一個Activity獲取一個
Fragment的所以圖片請求,將根據RequestManagerFragment的生命周期,統一管理圖片請求。
第四部分:監聽網絡狀態并作出相應
RequestManager的構造函數有如下方法:
//省略部分代碼...//監聽網絡變化的類ConnectivityMonitor connectivityMonitor = factory.build(context,new RequestManagerConnectivityListener(requestTracker));//省略部分代碼...lifecycle.addListener(connectivityMonitor); //省略部分代碼...復制代碼以上代碼可看出,ConnectivityMonitor也實現了LifecycleListener。繼續跟蹤代碼發現,factory的實例是ConnectivityMonitorFactory,在該工廠中會檢查網絡權限,同時創建ConnectivityMonitor的實例DefaultConnectivityMonitor。LifecycleListener接口的實現如下:
public void onStart() {register(); //register()方法為注冊廣播監聽網絡變化}public void onStop() {unregister(); //解除監聽廣播}public void onDestroy() {// Do nothing.}復制代碼廣播接收器代碼如下:
private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {public void onReceive(Context context, Intent intent) {boolean wasConnected = isConnected;isConnected = isConnected(context);if (wasConnected != isConnected) { //當網絡狀態發生變化時,才調用listener.onConnectivityChanged()方法listener.onConnectivityChanged(isConnected);}}};復制代碼ConnectivityListener 的實例的類型是RequestManager的內部類,代碼如下:
private static class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener {private final RequestTracker requestTracker;public RequestManagerConnectivityListener(RequestTracker requestTracker) {this.requestTracker = requestTracker;}public void onConnectivityChanged(boolean isConnected) {if (isConnected) { //如果當前狀態是鏈接狀態requestTracker.restartRequests(); //重新開啟圖片請求}}}復制代碼小結
以上就是Glide實現圖片加載和Activity、Fragment生命周期綁定的全部分析。會發現使用了觀察者模式和工廠模式進行解耦,其中創建一個不可見的Fragment設置到需要被監控生命周期的Activity、Fragment中,最為精彩。接下來將分析Glide核心設計二:圖片緩存。敬請期待。
轉載于:https://juejin.im/post/58abfbe95c497d005f732e0d
總結
以上是生活随笔為你收集整理的Glide核心设计一:皮皮虾,我们走的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从一道面试题,到“我可能看了假源码[2]
- 下一篇: Java:POI方式实现Word转htm