跟著源碼學設計:Glide框架及源碼解析(二)

前言
近期研究了一下Glide的圖片加載框架,在這里和大家分享一下。由于代碼研讀有限,難免有錯誤的地方,了解的童鞋還望指正。

本篇是Glide框架及源碼解析的第二篇,更多文章敬請關注后續文章。如果這篇文章對大家學習Glide有幫助,還望大家多多轉載。

版權歸作者所有,如有轉發,請注明文章出處:http://www.lxweimin.com/u/d43d948bef39

相關文章:

跟著源碼學設計:Glide框架及源碼解析(一)
跟著源碼學設計:Glide框架及源碼解析(二)
跟著源碼學設計:Glide框架及源碼解析(三)
跟著源碼學設計:Glide框架及源碼解析(四)
跟著源碼學設計:Glide框架及源碼解析(五)

1. Request管理機制

在上一篇中我們剖析了Glide的生命周期綁定機制,這一篇我們緊接著Glide的處理流程來學習一下Glide的請求管理機制。
我們先來看一下Glide的最簡單的使用代碼示例:

ImageView ivImage = (ImageView) findViewById(R.id.ivImage);
Glide.with(ivImage.getContext()) //獲取RequestManager對象
     .load(url); //設置Request對象需要的資源鏈接
     .into(ivImage); //獲取Request對象并綁定viewTarget -> 發起網絡請求

該段代碼十分簡潔,但是內部實現的功能卻十分的強大,比如:

  • request的生命周期管理(如:退出或者隱藏了界面,需求就取消或暫停了)
  • viewTarget的生命周期管理
  • 資源的復用和釋放
  • 靈活的配置(request的builder模式)

針對這些問題,后面將會一一展開剖析。

2. request及其生命周期管理

  • 通過上一篇的學習,我們知道了Glide內部生命周期接口為LifecycleListener
  • RequestManager具有生命周期(實現了LifecycleListener接口)
  • request由RequestManager的into()方法族獲得
  • request的生命周期由RequestManager統一管理

3. Glide請求管理機制類圖

RequestManager是如何生成request并管理request隊列的?
老規矩,先上圖:


Glide請求管理機制類圖

如圖:

  1. RequestManager持有一個RequestTracker對象requestTracker。
  2. requestTracker對象維護request的隊列集合
  3. RequestManager的load()函數用于獲取GenericRequestBuilder對象(其實是子類對象)
  • load()內部調用loadGeneric()方法,將requestTracker對象引用傳遞給GenericRequestBuilder類
  • load()實際調用GenericRequestBuilder.load()方法完成request的URL設置
  1. GenericRequestBuilder的into()方法是實際產生request和消費request的地方。
  • GenericRequestBuilder的into(target)方法調用obtainReauest()獲取到GenericRequest對象request,request與target相互綁定并被requestTracker維護。

3.1 GenericRequestBuilder的into(target)方法

public <Y extends Target<TranscodeType>> Y into(Y target) {
    Util.assertMainThread();
    if (target == null) {
      throw new IllegalArgumentException("You must pass in a non null Target");
    }
    if (!isModelSet) {
      throw new IllegalArgumentException("You must first set a model (try #load())");
    }
  
    //因為target和request是相互綁定的,所以考慮到復用的情景時,可以先獲取一下request
    Request previous = target.getRequest();
   
    //previous != null說明target有復用,需要釋放之前綁定的資源
    //注意:request內部是綁定了資源的,這里還沒有講到,先知道這回事,后面會講
    if (previous != null) {
        //釋放資源,防內存泄漏
        //這段代碼是精華,需要好好體會:
        //Glidek肯定支持view的復用(對吧?),那么復用的view資源是如何綁定和釋放的?
        //這里就是資源釋放的地方(入口),資源在何時綁定會在后續的課程講到。
        previous.clear();
        requestTracker.removeRequest(previous);
        previous.recycle();
    }
   
    //獲取需求對象(對象綁定了target)
    Request request = buildRequest(target);
    //target綁定需求
    target.setRequest(request);
    //因為target具有生命周期,即實現了LifecycleListener方法,所以將其注冊給ActivityFragmentLifecycle統一管理(不知道是啥的去看上一篇文章)
    lifecycle.addListener(target);
    //將需求加入隊列并執行需求
    //注意是單線程
    requestTracker.runRequest(request);
  
    return target;
}

3.2 request的生命周期管理

  • 根據上文得知,request都被加入到requestTracker中來管理
  • requestTracker由RequestManager創建和管理
  • RequestManager具有生命周期

3.2.1 RequestManager

下面讓我們看看RequestManager在各個生命周期回調里都做了什么

@Override
public void onStart() {
    resumeRequests();
}
 
@Override
public void onStop() {
    pauseRequests();
}
 
@Override
public void onDestroy() {
    requestTracker.clearRequests();
}
 
public void pauseRequests() {
    Util.assertMainThread();
    requestTracker.pauseRequests();
}
 
public void resumeRequests() {
    Util.assertMainThread();
    requestTracker.resumeRequests();
}
//下面的兩個回調其實和request的關系沒有那么直接,先放在這里留個印象    
public void onTrimMemory(int level) {
    glide.trimMemory(level);
}
 
public void onLowMemory() {
    glide.clearMemory();
}

代碼很清楚了吧。細心的同學可能注意到了onTrimMemory(int level)和onLowMemory(),這倆貨是系統在資源不足時調用的,說白了就是釋放內存,具體怎么搞得,后續文章會專門講到Glide的內存管理機制(也是精華)

3.2.2 requestTracker

最后讓我們看看requestTracker都干啥了吧

public class RequestTracker {
    private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
  
    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
    private final List<Request> pendingRequests = new ArrayList<Request>();
  
    private boolean isPaused;
  
    /**
    * Starts tracking the given request.
    */
    public void runRequest(Request request) {
        requests.add(request);
        if (!isPaused) {
            request.begin();
        } else {
            pendingRequests.add(request);
        }
    }
  
    // Visible for testing.
    void addRequest(Request request) {
        requests.add(request);
    }
  
    /**
    * Stops tracking the given request.
    */
    public void removeRequest(Request request) {
        requests.remove(request);
        pendingRequests.remove(request);
    }
   
    /**
    * Returns {@code true} if requests are currently paused, and {@code false} otherwise.
    */
    public boolean isPaused() {
        return isPaused;
    }
   
    /**
    * Stops any in progress requests.
    */
    public void pauseRequests() {
        isPaused = true;
        for (Request request : Util.getSnapshot(requests)) {
            if (request.isRunning()) {
                request.pause();
                pendingRequests.add(request);
            }
        }
    }
  
    /**
    * Starts any not yet completed or failed requests.
    */
    public void resumeRequests() {
        isPaused = false;
        for (Request request : Util.getSnapshot(requests)) {
            if (!request.isComplete() && !request.isCancelled() && !request.isRunning()) {
            request.begin();
            }
        }
        pendingRequests.clear();
    }
  
    /**
    * Cancels all requests and clears their resources.
    */
    public void clearRequests() {
        for (Request request : Util.getSnapshot(requests)) {
            request.clear();
        }
        pendingRequests.clear();
    }
  
    /**
    * Restarts failed requests and cancels and restarts in progress requests.
    */
    public void restartRequests() {
      for (Request request : Util.getSnapshot(requests)) {
          if (!request.isComplete() && !request.isCancelled()) {
              // Ensure the request will be restarted in onResume.
              request.pause();
              if (!isPaused) {
                  request.begin();
              } else {
                  pendingRequests.add(request);
              }
           }
        }
    }
}

(本篇是Glide框架及源碼解析的第二篇,更多文章敬請關注后續文章。版權歸作者所有,如有轉發,請注明文章出處:原文鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內容