Glide核心設計一:皮皮蝦,我們走

NEXT發表於2017-02-21

原文連結:Glide核心設計一:皮皮蝦,我們走

引言

皮皮蝦,又名蝦姑,是淡水中的強者。其頭部的兩個錘節,可以輕易破壞貝類的外殼,身體上的步足可以保證快速移動。這些優秀的品質,使它被表情包盯上。

Glide,作為Android最優秀的圖片載入框架之一,能和Activity和Fragment的生命週期繫結,是區別於其它網路框架的核心特徵,也是本文分析的重點。

我們將此特徵和皮皮蝦表情包做一個類比:

Glide核心設計一:皮皮蝦,我們走
皮皮蝦和Glide類比

圖片網路請求緊跟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核心設計一:皮皮蝦,我們走
宣告週期繫結類圖

類的簡單介紹

  1. RequestManagerRetriever:該類用於建立RequestManager或在Activity、Fragment中找出已建立的RequestManager,RequestManagerRetriever是一個單例。
  2. RequestManagerFragment:繼承Fragment,不可見,僅用於儲存RequestManager,還有一個SupportRequestManagerFragment繼承v4包的Fragment,功能類似。
  3. LifecycleListener:用於監聽Activity、Fragment的生命週期事件。
  4. Lifecycle:用於新增LifecycleListener。
  5. ActivityFragmentLifecycle:實現Livecycle介面,用於通知Activity、Fragment的生命週期事件。
  6. RequestTracker:該類用於跟蹤、取消和重新啟動執行中、已完成和失敗的請求。
  7. ConnectivityMonitor: 監聽網路事件的介面,當網路狀態發生變化時,影響網路請求狀態,繼承LifecycleListener。
  8. DefaultConnectivityMonitor: ConnectivityMonitor的實現類,實現監聽網路狀態的邏輯。
  9. RequestManagerConnectivityListener: 實現ConnectivityListener介面,將網路事件傳遞給RequestTracker。

類的聯絡

以上對各類有一個簡單的瞭解,接下來將重點講清楚各類之間的聯絡。整個生命週期的繫結分為四部分。

  1. 呼叫Glide.with(Context),根據傳入的Context型別建立RequestManager。Context可以為Activity、Fragment和Application。
  2. 在傳入的引數Activity、或者Fragment中,新增一個不可見的Fragment,監聽不可見Fragment的生命週期並將該事件傳遞給和Fragment一一繫結的RequestManager。
  3. RequestManager監聽到生命事件後,管理圖片請求做出響應。
  4. 監聽當網路從無到有時,RequestManager要重新啟動圖片請求。

程式碼解讀

根據以上內容可直接跟程式碼可跳過以下內容,印象更加深刻。

第一部分: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) {  //傳入的是Fragment
                return get((FragmentActivity) context);
            } else if (context instanceof Activity) {  //傳入的是Acitivity
                return get((Activity) context);
            } else if (context instanceof ContextWrapper) { //傳入的是Application
                return 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();   //獲取FragmentManager
            return fragmentGet(activity, fm); 
        }
    }複製程式碼

主要呼叫fragmentGet方法,程式碼如下:

 RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
        RequestManagerFragment current = getRequestManagerFragment(fm); 
        RequestManager requestManager = current.getRequestManager();  //根據RequestManagerFragment獲取RequestManager,一個RequestManagerFragment包含一個RequestManager
        if (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生命週期相關的程式碼如下:

  @Override
     public void onStart() {
         super.onStart();
         lifecycle.onStart(); //執行lifecycle的onStart方法
     }

     @Override
     public void onStop() {
         super.onStop();
         lifecycle.onStop();//執行lifecycle的onStop方法
     }

     @Override
     public void onDestroy() {
         super.onDestroy();
         lifecycle.onDestroy();//執行lifecycle的onDestroy方法
     }複製程式碼

可以看出,Fragment的宣告週期的監聽都轉移到型別是ActivityFragmentLifecycle的變數lifecycle中的對應方法執行。檢視ActivityFragmentLifecycle的程式碼:

  void onStart() {
        isStarted = true;
        //迴圈set集合lifecycleListeners中所有LifecycleListener,執行對應的onStart
        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
            lifecycleListener.onStart();
        }
    }

   //省略onStop()和onDestroy()方法,和onStart()方法類似。複製程式碼

set集合的LifecycleListener是如何新增進去的,看ActivityFragmentLifecycle中的程式碼:

 @Override
     public void addListener(LifecycleListener listener) {
         lifecycleListeners.add(listener);
         if (isDestroyed) {//如果當前頁面已經被destroy,則呼叫對應的onDestroy
             listener.onDestroy();
         } else if (isStarted) {//如果當前頁面已經開啟,則呼叫對應的onStart
             listener.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() {
                @Override
                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的方法:

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

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

    @Override
    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介面的實現如下:

 @Override
    public void onStart() {
        register(); //register()方法為註冊廣播監聽網路變化
    }

    @Override
    public void onStop() {
        unregister(); //解除監聽廣播
    }

    @Override
    public void onDestroy() {
        // Do nothing.
    }複製程式碼

廣播接收器程式碼如下:

  private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {
        @Override
        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;
        }

        @Override
        public void onConnectivityChanged(boolean isConnected) {
            if (isConnected) { //如果當前狀態是連結狀態
                requestTracker.restartRequests(); //重新開啟圖片請求
            }
        }
    }複製程式碼

小結

以上就是Glide實現圖片載入和Activity、Fragment生命週期繫結的全部分析。會發現使用了觀察者模式和工廠模式進行解耦,其中建立一個不可見的Fragment設定到需要被監控生命週期的Activity、Fragment中,最為精彩。接下來將分析Glide核心設計二:圖片快取。敬請期待。

相關文章