Glide加載過程源碼導(dǎo)讀

Glide加載過程源碼導(dǎo)讀:
咱們從最常用的使用方式開始走起躬翁,
即 Glide.with(this).load("https://xxxx.jpg").into(imageView);

Glide.with(this) 會根據(jù)傳入的參數(shù)不同創(chuàng)建不同的 RequestManager 對象并返回
主要有以下幾種情形:
1.Fragment:則使用 Fragment.getChildFragmentManager() 向Fragment中添加一個子Fragment(只創(chuàng)建一次焦蘑,再次調(diào)用時會先查找該子Fragment是否已存在),用于監(jiān)聽該Fragment的生命周期

2.FragmentActivity/Activity:則使用 FragmentActivity.getSupportFragmentManager()或者Activity.getFragmentManager() 向 Activity 中添加一個Fragment(只創(chuàng)建一次盒发,再次調(diào)用時會先查找該子Fragment是否已存在)例嘱,用于監(jiān)聽該Activity的生命周期

3.View:會查找該view是屬于哪一個Fragment或者Activity,然后再依據(jù)1和2的情況

4.Context:會判斷是否是Activity迹辐,則執(zhí)行第二種情形蝶防;如果不是則使用 Context.getApplicationContext() 創(chuàng)建一個和Application生命周期一致的RequestManager對象甚侣,該對象只會創(chuàng)建一次保存在 RequestManagerRetriever.applicationManager變量中

5.如果在子線程調(diào)用 with方法則會直接創(chuàng)建或返回和Application生命周期一致的RequestManager對象

接下來看看 RequestManager.load() 方法中做了些什么明吩,
首先調(diào)用 RequestManager.asDrawable() 方法返回一個 RequestBuilder 對象
接著執(zhí)行 RequestBuilder.load() 該方法內(nèi)部調(diào)用了 RequestBuilder.loadGeneric() 方法,loadGeneric方法中主要記錄了加載時傳入的 model

public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
}
// 在load中調(diào)用了 asDrawable() 方法殷费,該方法最終執(zhí)行到下面的語句印荔,
// 即:創(chuàng)建了一個 RequestBuilder 對象,而傳入的 resourceClass 為 Drawable.class
return new RequestBuilder<>(glide, this, resourceClass, context);

// 然后執(zhí)行 RequestBuilder.load(Object model) 方法详羡,該方法直接調(diào)用到下面的RequestBuilder.loadGeneric
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    if (isAutoCloneEnabled()) {
      return clone().loadGeneric(model);
    }
    // 只是簡單記錄了 model
    this.model = model;
    // 并設(shè)置  isModelSet = true
    isModelSet = true;
    // 然后返回 this
    return selfOrThrowIfLocked();
}

接下來執(zhí)行 RequestBuilder.into(ImageView view) 方法仍律,該方法內(nèi)部
首先,創(chuàng)建了一個 Request, 不考慮error实柠,thumbnail 的情況下水泉,最終會創(chuàng)建一個 SingleRequest 對象,該對象保存了請求相關(guān)的 context,model草则,transcodeClass钢拧,requestOptions,target炕横,targetListener源内,requestListeners等相關(guān)信息膜钓,error,thumbnail 的情形下也只是對多個 SingleRequest 進(jìn)行了組合以形成一個更為復(fù)雜的 Request颂斜;
然后焚鲜,執(zhí)行 RequestManager.track(target, request); 在track方法中最終調(diào)用 Request.begin()方法啟動本次請求忿磅;

// 葱她,該方法內(nèi)部又調(diào)用了下面的方法
private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target, 
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    // 此處 callbackExecutor 主要用于圖片加載完成后通知加載結(jié)果
    // 調(diào)用 into(ImageView view)方法時傳入的是Executors.mainThreadExecutor()吨些,即在主線程顯示圖片
    ....
    // 創(chuàng)建 Request, 不考慮error炒辉,thumbnail 的情況下黔寇,最終會創(chuàng)建一個 SingleRequest 對象缝裤,
    // 該對象保存了請求相關(guān)的 context,model霎苗,transcodeClass唁盏,requestOptions,target答倡,targetListener瘪撇,requestListeners等相關(guān)信息
    // error港庄,thumbnail 的情形下也只是對多個 SingleRequest 進(jìn)行了組合以形成一個更為復(fù)雜的 Request
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    // 獲取與target綁定的上一次請求鹏氧,并做一些比較把还,判斷是否是相同的請求
    Request previous = target.getRequest();
    ....
    requestManager.clear(target);
    // 將request綁定到target上,
    // 如果是CustomViewTarget安皱,則主要使用了 View.setTag(int key, final Object tag)方法
    target.setRequest(request);
    // 在track方法中最終調(diào)用 Request.begin()方法啟動本次請求
    requestManager.track(target, request);
    return target;
  }

接下來看 SingleRequest.begin() 方法酌伊,
首先缀踪,獲取 target 的寬高
然后驴娃,在 SingleRequest.onSizeReady()方法中調(diào)用 engine.load() 方法

public void begin() {
    synchronized (requestLock) {
      ....
      status = Status.WAITING_FOR_SIZE;
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        // 如果寬高有效,則開始加載蔗草,所以真正的加載是在 onSizeReady 方法中
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        // 先獲取寬高信息蕉世,獲取結(jié)果會通過 onSizeReady 回調(diào)通知
        target.getSize(this);
      }
      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) {
        // 開始加載回調(diào)通知
        target.onLoadStarted(getPlaceholderDrawable());
      }
    }
  }

// 在 SingleRequest.onSizeReady 中調(diào)用 Engine.load 方法開始加載
public void onSizeReady(int width, int height) {
    synchronized (requestLock) {
      ....
      // 調(diào)用 Engine.load 方法開始加載
      loadStatus = engine.load(....);
        ....
    }
  }

Engine.load()方法
首先婆硬,從內(nèi)存中加載(loadFromMemory)彬犯,如果內(nèi)存中沒有谐区,再啟動異步加載
異步加載最終調(diào)用 EngineJob.start(DecodeJob decodeJob) 方法內(nèi)部會根據(jù)加載階段選擇合適的 executor 執(zhí)行 decodeJob:
加載階段主要分為:
1.從磁盤緩存的已經(jīng)過采樣和轉(zhuǎn)碼的 (downsampled/transformed) 文件加載
2.從磁盤緩存的源文件(沒做過任何修改的) 加載
3.從最原始的文件來源(網(wǎng)絡(luò)宋列、磁盤文件等)加載

public <R> LoadStatus load(.... ResourceCallback cb ....) {
     // 這里的 ResourceCallback 參數(shù)傳入的是 SingleRequest 對象
    ....
    // 根據(jù)請求的相關(guān)信息構(gòu)建緩存的 key 
    EngineKey key =keyFactory.buildKey(....);
    EngineResource<?> memoryResource;
    synchronized (this) {
      // 先查詢 內(nèi)存中是否已存在本次請求的資源
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
      if (memoryResource == null) {
          // 如果內(nèi)存中不存在本次請求的資源,則開啟異步加載
        return waitForExistingOrStartNewJob(....);
      }
    }
    // 如果內(nèi)存中 已存在 本次請求的資源灭返,則直接通知回調(diào)接口
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE, false);
    return null;
  }

// Engine.waitForExistingOrStartNewJob()
private <R> LoadStatus waitForExistingOrStartNewJob(.... ResourceCallback cb ....) {
    // 這里的 ResourceCallback 參數(shù)傳入的是 SingleRequest 對象
    ....
    // 根據(jù) key 查詢是否已經(jīng)存在 EngineJob 熙含,
    // 如果已存在則不用開啟新的 EngineJob 去加載相同的資源了艇纺,
    // 只需要將回調(diào)接口添加到 EngineJob 以便于加載成功后通知
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb, callbackExecutor);
      return new LoadStatus(cb, current);
    }
    // 創(chuàng)建一個 EngineJob 對象
    EngineJob<R> engineJob = engineJobFactory.build(key ....);
    // 創(chuàng)建 DecodeJob 對象蚓聘,真正的異步加載(從磁盤/網(wǎng)絡(luò)等)都是在 DecodeJob 中完成的盟劫,
    // DecodeJob 實現(xiàn) Runnable 捞高,所以可以在 executor 中執(zhí)行
    // EngineJob 是實現(xiàn)了 DecodeJob.Callback 的,后續(xù) DecodeJob 加載流程中會用于切換 executor氢哮,或者接收加載成功/失敗的回調(diào)
    DecodeJob<R> decodeJob = decodeJobFactory.build(.... key .... engineJob);
    ....
    // 將 engineJob 加入 jobs 
    jobs.put(key, engineJob);
    // 將回調(diào)接口添加到 EngineJob 以便于加載成功后通知
    engineJob.addCallback(cb, callbackExecutor);
    // EngineJob.start 方法內(nèi)部會根據(jù)加載階段選擇合適的 executor 執(zhí)行 decodeJob
    // 加載階段主要分為:
    // 1.從磁盤緩存的已經(jīng)過采樣和轉(zhuǎn)碼的 (downsampled/transformed) 文件加載
    // 2.從磁盤緩存的源文件(沒做過任何修改的) 加載
    // 3.從最原始的文件來源(網(wǎng)絡(luò)冗尤、磁盤文件等)加載
    engineJob.start(decodeJob);
    return new LoadStatus(cb, engineJob);
  }

接下來看 DecodeJob.run 方法
DecodeJob 會根據(jù)磁盤緩存策略胀溺,嘗試不同的加載來源
默認(rèn)磁盤緩存策略下 DiskCacheStrategy.decodeCachedResource() 和 DiskCacheStrategy.decodeCachedData() 都返回 true仓坞,
也即 先嘗試從磁盤緩存的已經(jīng)過采樣和轉(zhuǎn)碼的 (downsampled/transformed) 文件加載无埃,
如果緩存中沒有,再嘗試從磁盤緩存的源文件(沒做過任何修改的) 加載侦镇,
如果緩存中依然沒有,再嘗試從最原始的文件來源(網(wǎng)絡(luò)震捣、磁盤文件等)加載蒿赢。

public void run() {
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      // 加載的邏輯在 runWrapped 方法中
      runWrapped();
    } catch (Throwable t) {
    } finally {
        // DataFetcher 是真正用來從磁盤或網(wǎng)絡(luò)加載文件的剩胁,
        // 其內(nèi)部可能打開了一些 InputStream 等資源昵观,
        // 所以必須調(diào)用其 cleanup 方法給其一個關(guān)閉資源的機(jī)會
        if (localFetcher != null) {
            localFetcher.cleanup();
        }
    }
}

// DecodeJob.runWrapped 方法
private void runWrapped() {
    // DecodeJob創(chuàng)建時啊犬,runReason 的初始值是 RunReason.INITIALIZE
    switch (runReason) {
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
}

// DecodeJob.getNextStage 方法
private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        // 默認(rèn)配置中 decodeCachedResource 返回 true
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE
            : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        // 默認(rèn)配置中 decodeCachedData 返回 true
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE
            : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // RequestBuilder 可以配置 onlyRetrieveFromCache,其默認(rèn)值為 false
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
}

// DecodeJob.getNextGenerator
private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        // ResourceCacheGenerator 負(fù)責(zé)從磁盤緩存的已經(jīng)過采樣和變換的 (downsampled/transformed) 文件加載
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        // DataCacheGenerator 負(fù)責(zé)從磁盤緩存的源文件(沒做過任何修改的) 加載
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        // SourceGenerator 負(fù)責(zé)從最原始的文件來源(網(wǎng)絡(luò)、用戶手機(jī)中的文件等)加載
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
}

DecodeJob 的執(zhí)行流程比較繞峻贮,我繪制了一個執(zhí)行流程圖如下:


glide_decode_job.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纤控,一起剝皮案震驚了整個濱河市船万,隨后出現(xiàn)的幾起案子骨田,更是在濱河造成了極大的恐慌态贤,老刑警劉巖抵卫,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胎撇,死亡現(xiàn)場離奇詭異,居然都是意外死亡雅采,警方通過查閱死者的電腦和手機(jī)婚瓜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門巴刻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胡陪,“玉大人柠座,你說我怎么就攤上這事片橡。” “怎么了吹泡?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵爆哑,是天一觀的道長泪漂。 經(jīng)常有香客問我歪泳,道長,這世上最難降的妖魔是什么敌卓? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任瘪吏,我火速辦了婚禮掌眠,結(jié)果婚禮上蓝丙,老公的妹妹穿的比我還像新娘望拖。我一直安慰自己说敏,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布医咨。 她就那樣靜靜地躺著腋逆,像睡著了一般惩歉。 火紅的嫁衣襯著肌膚如雪撑蚌。 梳的紋絲不亂的頭發(fā)上争涌,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音饮潦,去河邊找鬼携狭。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的稀并。 我是一名探鬼主播仅颇,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碘举!你這毒婦竟也來了忘瓦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤殴俱,失蹤者是張志新(化名)和其女友劉穎政冻,沒想到半個月后枚抵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體线欲,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年汽摹,在試婚紗的時候發(fā)現(xiàn)自己被綠了李丰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呐舔,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響盅视,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧埃撵,春花似錦捂刺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刻炒。三九已至,卻和暖如春晒喷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背爷抓。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留极颓,地道東北人兵琳。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親曙博。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

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