跟著源碼學(xué)設(shè)計(jì):Glide框架及源碼解析(二)

前言
近期研究了一下Glide的圖片加載框架姜胖,在這里和大家分享一下钦勘。由于代碼研讀有限示血,難免有錯(cuò)誤的地方棋傍,了解的童鞋還望指正。

本篇是Glide框架及源碼解析的第二篇难审,更多文章敬請(qǐng)關(guān)注后續(xù)文章瘫拣。如果這篇文章對(duì)大家學(xué)習(xí)Glide有幫助,還望大家多多轉(zhuǎn)載告喊。

版權(quán)歸作者所有麸拄,如有轉(zhuǎn)發(fā),請(qǐng)注明文章出處:http://www.reibang.com/u/d43d948bef39

相關(guān)文章:

跟著源碼學(xué)設(shè)計(jì):Glide框架及源碼解析(一)
跟著源碼學(xué)設(shè)計(jì):Glide框架及源碼解析(二)
跟著源碼學(xué)設(shè)計(jì):Glide框架及源碼解析(三)
跟著源碼學(xué)設(shè)計(jì):Glide框架及源碼解析(四)
跟著源碼學(xué)設(shè)計(jì):Glide框架及源碼解析(五)

1. Request管理機(jī)制

在上一篇中我們剖析了Glide的生命周期綁定機(jī)制黔姜,這一篇我們緊接著Glide的處理流程來學(xué)習(xí)一下Glide的請(qǐng)求管理機(jī)制拢切。
我們先來看一下Glide的最簡單的使用代碼示例:

ImageView ivImage = (ImageView) findViewById(R.id.ivImage);
Glide.with(ivImage.getContext()) //獲取RequestManager對(duì)象
     .load(url); //設(shè)置Request對(duì)象需要的資源鏈接
     .into(ivImage); //獲取Request對(duì)象并綁定viewTarget -> 發(fā)起網(wǎng)絡(luò)請(qǐng)求

該段代碼十分簡潔,但是內(nèi)部實(shí)現(xiàn)的功能卻十分的強(qiáng)大秆吵,比如:

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

針對(duì)這些問題,后面將會(huì)一一展開剖析纳寂。

2. request及其生命周期管理

  • 通過上一篇的學(xué)習(xí)主穗,我們知道了Glide內(nèi)部生命周期接口為LifecycleListener
  • RequestManager具有生命周期(實(shí)現(xiàn)了LifecycleListener接口)
  • request由RequestManager的into()方法族獲得
  • request的生命周期由RequestManager統(tǒng)一管理

3. Glide請(qǐng)求管理機(jī)制類圖

RequestManager是如何生成request并管理request隊(duì)列的?
老規(guī)矩毙芜,先上圖:


Glide請(qǐng)求管理機(jī)制類圖

如圖:

  1. RequestManager持有一個(gè)RequestTracker對(duì)象requestTracker忽媒。
  2. requestTracker對(duì)象維護(hù)request的隊(duì)列集合
  3. RequestManager的load()函數(shù)用于獲取GenericRequestBuilder對(duì)象(其實(shí)是子類對(duì)象)
  • load()內(nèi)部調(diào)用loadGeneric()方法,將requestTracker對(duì)象引用傳遞給GenericRequestBuilder類
  • load()實(shí)際調(diào)用GenericRequestBuilder.load()方法完成request的URL設(shè)置
  1. GenericRequestBuilder的into()方法是實(shí)際產(chǎn)生request和消費(fèi)request的地方腋粥。
  • GenericRequestBuilder的into(target)方法調(diào)用obtainReauest()獲取到GenericRequest對(duì)象request晦雨,request與target相互綁定并被requestTracker維護(hù)。

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())");
    }
  
    //因?yàn)閠arget和request是相互綁定的隘冲,所以考慮到復(fù)用的情景時(shí)闹瞧,可以先獲取一下request
    Request previous = target.getRequest();
   
    //previous != null說明target有復(fù)用,需要釋放之前綁定的資源
    //注意:request內(nèi)部是綁定了資源的对嚼,這里還沒有講到夹抗,先知道這回事,后面會(huì)講
    if (previous != null) {
        //釋放資源纵竖,防內(nèi)存泄漏
        //這段代碼是精華漠烧,需要好好體會(huì):
        //Glidek肯定支持view的復(fù)用(對(duì)吧杏愤?),那么復(fù)用的view資源是如何綁定和釋放的已脓?
        //這里就是資源釋放的地方(入口)珊楼,資源在何時(shí)綁定會(huì)在后續(xù)的課程講到。
        previous.clear();
        requestTracker.removeRequest(previous);
        previous.recycle();
    }
   
    //獲取需求對(duì)象(對(duì)象綁定了target)
    Request request = buildRequest(target);
    //target綁定需求
    target.setRequest(request);
    //因?yàn)閠arget具有生命周期度液,即實(shí)現(xiàn)了LifecycleListener方法厕宗,所以將其注冊(cè)給ActivityFragmentLifecycle統(tǒng)一管理(不知道是啥的去看上一篇文章)
    lifecycle.addListener(target);
    //將需求加入隊(duì)列并執(zhí)行需求
    //注意是單線程
    requestTracker.runRequest(request);
  
    return target;
}

3.2 request的生命周期管理

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

3.2.1 RequestManager

下面讓我們看看RequestManager在各個(gè)生命周期回調(diào)里都做了什么

@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();
}
//下面的兩個(gè)回調(diào)其實(shí)和request的關(guān)系沒有那么直接堕担,先放在這里留個(gè)印象    
public void onTrimMemory(int level) {
    glide.trimMemory(level);
}
 
public void onLowMemory() {
    glide.clearMemory();
}

代碼很清楚了吧已慢。細(xì)心的同學(xué)可能注意到了onTrimMemory(int level)和onLowMemory(),這倆貨是系統(tǒng)在資源不足時(shí)調(diào)用的霹购,說白了就是釋放內(nèi)存佑惠,具體怎么搞得,后續(xù)文章會(huì)專門講到Glide的內(nèi)存管理機(jī)制(也是精華)

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框架及源碼解析的第二篇齐疙,更多文章敬請(qǐng)關(guān)注后續(xù)文章膜楷。版權(quán)歸作者所有,如有轉(zhuǎn)發(fā)贞奋,請(qǐng)注明文章出處:原文鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赌厅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子轿塔,更是在濱河造成了極大的恐慌特愿,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件催训,死亡現(xiàn)場離奇詭異洽议,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)漫拭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門亚兄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人采驻,你說我怎么就攤上這事审胚。” “怎么了礼旅?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵膳叨,是天一觀的道長。 經(jīng)常有香客問我痘系,道長菲嘴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮龄坪,結(jié)果婚禮上昭雌,老公的妹妹穿的比我還像新娘。我一直安慰自己健田,他們只是感情好烛卧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妓局,像睡著了一般总放。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上好爬,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天局雄,我揣著相機(jī)與錄音,去河邊找鬼抵拘。 笑死哎榴,一個(gè)胖子當(dāng)著我的面吹牛型豁,可吹牛的內(nèi)容都是我干的僵蛛。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼迎变,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼充尉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衣形,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤驼侠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后谆吴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倒源,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年句狼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了笋熬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腻菇,死狀恐怖胳螟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筹吐,我是刑警寧澤糖耸,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站丘薛,受9級(jí)特大地震影響嘉竟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一舍扰、第九天 我趴在偏房一處隱蔽的房頂上張望铡俐。 院中可真熱鬧,春花似錦妥粟、人聲如沸审丘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滩报。三九已至,卻和暖如春播急,著一層夾襖步出監(jiān)牢的瞬間脓钾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國打工桩警, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留可训,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓捶枢,卻偏偏與公主長得像握截,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子烂叔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容