Android 圖片載入框架Glide4.0源碼完全解析(一)

来源:http://www.cnblogs.com/guanmanman/archive/2017/06/14/7008259.html
-Advertisement-
Play Games

上一篇博文寫的是Picasso基本使用和源碼完全解析,Picasso的源碼閱讀起來還是很順暢的,然後就想到Glide框架,網上大家也都推薦使用這個框架用來載入圖片,正好我目前的寫作目標也是分析當前一些流行的框架源碼,那就也來解析下Glide的源碼吧,而且有了Picasso源碼的分析相信很快就搞定Gl... ...


寫在之前

上一篇博文寫的是Picasso基本使用和源碼完全解析,Picasso的源碼閱讀起來還是很順暢的,然後就想到Glide框架,網上大家也都推薦使用這個框架用來載入圖片,正好我目前的寫作目標也是分析當前一些流行的框架源碼,那就也來解析下Glide的源碼吧,而且有了Picasso源碼的分析相信很快就搞定Glide的,結果也就悲劇了,深陷其中無法自拔了,Glide的源碼遠非Picasso能比,閱讀起來也是相當的困難的,而且我使用的是最新的Glide4.0,與之前版本有較大的差異,網上也沒可以參考的資料,這就悲劇了,苦頭專研唄。直到今天才從深溝中冒出頭了,差點憋死,哈哈。

正文

Glide的使用和Picasso基本一樣,這裡就不再多說,主要是因為源碼分析會寫的很長很細,再加上基本使用的話,就更加長了,而且上一篇已寫過Picasso的基本使用,這兩者在使用方面相差的微乎及微,所以我們這篇文章直接進入源碼分析。

Glide 源碼分析

首先在build.gradle里添加如下引用:

compile 'com.github.bumptech.glide:glide:4.0.0-RC0'

這裡我用的是Glide4.0也是最新的版本,和3.X在源碼上還是有很大的差別的。看過3.X源碼的相信對比下這篇文章就會知道。

ok,和Picasso的分析模式一樣,我們也是從下麵最簡單的代碼進行一步一步的深入分析:

Glide.with(MainActivity.this).load(url).into(headerImage);

with()

首先我們來看看當我們調用Glide的with方法那做了哪些工作:

with方法中可以接受Context,Activity,FragmentActivity,Fragment甚至是View不同的類型,返回的是RequestManager對象,這個對象是需要在RequestManagerRetriever中獲取的,那我們在來看看RequestManagerRetriever是怎麼獲取到的?請看getRetriever方法的源碼:

getRetriever方法中也沒有真正的創建RequestManagerRetriever對象,而是從Glide的getRequestManagerRetriever方法中獲取,那麼很明顯的可以看出所做的工作都是在Glide的get方法中完成的,在來看下get方法源碼:

來到這一步,我們看到了非常熟悉的代碼設計原理,那就是雙重加鎖的單例模式保證Glide對象的唯一性,那麼initGlide就是創建Glide對象的方法了,請看:

這是initGlide方法中最重要的代碼,主要是創建了一個GlideBuilder對象,然後調用build方法來完成Glide對象的創建,相信不用看bulid方法,大家也會猜到接下來將要發生什麼樣的事情,沒錯,那就是使用建造者設計模式來完美的構建出Glide對象:

public Glide build(Context context) {
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }

    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }

    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }

    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }

    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      bitmapPool = new LruBitmapPool(size);
    }

    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    if (engine == null) {
      engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor,
          GlideExecutor.newUnlimitedSourceExecutor());
    }

    RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(
        requestManagerFactory);

    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptions.lock());
  }

build方法中主要是構建線程池(包括sourceExecutor ,diskCacheExecutor ),緩存大小和緩存器,預設的連接監聽工廠(connectivityMonitorFactory ),Engine對象和RequestManagerRetriever 對象等等。

有幾個重要的對象創建,我們這裡先看下它的構建內容:

1 Engine對象

創建Engine對象在構造方法中傳遞了幾個重要的參數,分別是線程池,記憶體緩存和硬碟緩存對象,那我們來看看在構造方法中它是怎麼構建Engine對象的:

Engine(MemoryCache cache,
      DiskCache.Factory diskCacheFactory,
      GlideExecutor diskCacheExecutor,
      GlideExecutor sourceExecutor,
      GlideExecutor sourceUnlimitedExecutor,
      Map<Key, EngineJob<?>> jobs,
      EngineKeyFactory keyFactory,
      Map<Key, WeakReference<EngineResource<?>>> activeResources,
      EngineJobFactory engineJobFactory,
      DecodeJobFactory decodeJobFactory,
      ResourceRecycler resourceRecycler) {
    this.cache = cache;
    this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory);

    if (activeResources == null) {
      activeResources = new HashMap<>();
    }
    this.activeResources = activeResources;

    if (keyFactory == null) {
      keyFactory = new EngineKeyFactory();
    }
    this.keyFactory = keyFactory;

    if (jobs == null) {
      jobs = new HashMap<>();
    }
    this.jobs = jobs;

    if (engineJobFactory == null) {
      engineJobFactory = new EngineJobFactory(diskCacheExecutor, sourceExecutor,
          sourceUnlimitedExecutor, this);
    }
    this.engineJobFactory = engineJobFactory;

    if (decodeJobFactory == null) {
      decodeJobFactory = new DecodeJobFactory(diskCacheProvider);
    }
    this.decodeJobFactory = decodeJobFactory;

    if (resourceRecycler == null) {
      resourceRecycler = new ResourceRecycler();
    }
    this.resourceRecycler = resourceRecycler;

    cache.setResourceRemovedListener(this);
  }

創建了幾個工廠對象方法,比如EngineKeyFactory,EngineJobFactory和DecodeJobFactory,幾個HashMap類型的對象集合,如:jobs ,activeResources 等等,然後就分別把這些對象賦值給Engine的成員變數,那麼來看下創建Engine對象時到底初始化了那些成員變數:

ok,Engine是一個非常重要的對象,後面扮演著重要的角色,為了方面理解它所擁有那些可使用的對象,這裡我做了個類圖顯示的標了出來。

2 RequestManagerRetriever 對象

再來看看RequestManagerRetriever 對象的創建,這個相對的簡單很多,我們來看下它的構造方法:

由於我們傳遞過來的requestManagerFactory為空,所以factory將會使用預設的DEFAULT_FACTORY工廠,DEFAULT_FACTORY是真正創建RequestManager對象的地方,稍後介紹。

這裡只是讓大家知道這裡的factory就是DEFAULT_FACTORY。

來看看它擁有哪些成員:

3 Glide對象

在build方法中 return new Gilde(),創建一個Glide對象並返回,那在Gilde構造方法中做了哪些初始化工作呢?

Glide(
      Context context,
      Engine engine,
      MemoryCache memoryCache,
      BitmapPool bitmapPool,
      ArrayPool arrayPool,
      RequestManagerRetriever requestManagerRetriever,
      ConnectivityMonitorFactory connectivityMonitorFactory,
      int logLevel,
      RequestOptions defaultRequestOptions) {
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.arrayPool = arrayPool;
    this.memoryCache = memoryCache;
    this.requestManagerRetriever = requestManagerRetriever;
    this.connectivityMonitorFactory = connectivityMonitorFactory;

    DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
    bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);

    final Resources resources = context.getResources();

    registry = new Registry();
    registry.register(new DefaultImageHeaderParser());

    registry.register()...append()...

    ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
    glideContext = new GlideContext(context, registry, imageViewTargetFactory,
        defaultRequestOptions, engine, this, logLevel);
  }

這裡首先是把傳遞進來的參數賦值給成員變數,然後創建了幾個重要的對象:

①:Registry對象

Registry主要是添加很多的註冊或解析方式等,這在後面用來解析是從記憶體,文件或是網路獲取資源有著重要的作用,而且它每一類解析方式都會提供多個方法,一種方式獲取不到將會使用另外一種,知道獲取到資源為止,來看下它的register和append方法:

主要是存放到不同的對象中的集合變數中。

②:GlideContext對象

GlideContext對象在後面也扮演中重要的角色,創建這個對象到目前為止只是為了初始化和賦值:

總結下Glide,Registry和GlideContext對象所初始化的參數:

這是到目前對象所擁有的成員方法和成員變數。

ok,再回到上面build方法,在返回Glide對象後,調用getRequestManagerRetriever()從而獲取到RequestManagerRetriever對象,從上面Glide類圖我們也可以看出,Glide對象已包含RequestManagerRetriever對象。

再往上返回一步,在getRetriever(activity)方法中獲取到RequestManagerRetriever對象後,調用get(activity)來獲取RequestManager對象,那麼我們來看看它是怎麼獲取到的?

首先判斷是否在子線程執行,否則就調用supportFragmentGet方法來獲取RequestManager對象,那麼來看下它的源碼:

還記得我們的RequestManagerRetriever擁有哪些成員嗎,不記得就去看看上面它的類圖吧,由它的源碼我們可以看到它將會使用factory並調用它的build方法,還記得factory是什麼嗎?上面已分析factory就是DEFAULT_FACTORY,那來看看它的源碼實現:

在build中創建一個RequestManager對象並返回,來看下RequestManager的構造方法中做了哪些操作:

 RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      RequestManagerTreeNode treeNode,
      RequestTracker requestTracker,
      ConnectivityMonitorFactory factory) {
    this.glide = glide;
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;

    final Context context = glide.getGlideContext().getBaseContext();

    connectivityMonitor =
        factory.build(context, new RequestManagerConnectivityListener(requestTracker));

    if (Util.isOnBackgroundThread()) {
      mainHandler.post(addSelfToLifecycle);
    } else {
      lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);

    setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());

    glide.registerRequestManager(this);
  }

主要是賦值,添加生命周期監聽器,設置請求屬性,以及註冊請求管理器,代碼還是很簡單的,都能看的明白。

來看下它的類圖:

ok,到此,我們的with方法中獲取RequestManager對象就已完成。

來看下with方法執行的順序圖:

註:流程圖看不清楚,可以選擇在“新標簽中打開圖片”查看。

load()

在調用with方法獲取到RequestManager對象的前提下,調用load方法,並傳遞我們的url參數,來看下它的源碼:

這裡並沒有直接的去載入我們的url獲取資源,而是首先調用asDrawable方法來配置圖片的信息,其實就是說載入獲取的圖片資源是轉換為drawale或是bitmap或是gif進行圖片顯示,預設的是使用drawale,你也可以使用asGif()/asBitmap()來設置它是已什麼形式來展示。這裡我們按預設的方式來分析。

load方法目的是為了獲取RequestBuilder對象,下麵來一步步分解它源碼:

首先來看下asDrawable()的源碼:

asDrawable()中首先調用as方法,並傳進Drawable.class作為參數,來看下as方法的源碼:

由as方法,我們可看到它直接的創建一個RequestBuilder對象,並傳遞了相關的參數進去,這裡要註意下resourceClass就是Drawable.class這裡在後面有個選擇分支時使用到。

來看下RequestBuilder的構造方法中做了哪些初始化佈局。

很簡單的賦值,這裡也需要註意的是transcodeClass就是Drawable.class類。

ok,獲取到RequestBuilder對象後,它又做了進一步的賦值操作,就是在transition方法中,

把創建的DrawableTransitionOptions對象賦值給transitionOptions變數。

ok,再往上來看,完成asDrawable方法對RequestBuilder的創建後才調用load方法來傳遞我們的url地址,其實在load也沒有做什麼事,就是一個中轉站,轉給了loadGeneric方法,來看:

在loadGeneric方法中也沒做其他太多的操作,也是保存了我們的url並且isModelSet設置為true,意思就是說Model已有設置了。來看下它的類圖:

它到目前為止所包含的成員變數和方法都在此。

ok,到此我們的load方法也分析完畢,來看下它的流程圖:

註:流程圖看不清楚,可以選擇在“新標簽中打開圖片”查看。

由於Glide源碼很是複雜,寫的很長,所以只能分兩篇來發佈,第一篇分析了Glide的with和load方法源碼,第二篇將會分析into方法,說實在的into方法複雜程度遠超過with和load方法總和,但是沒關係,還是保持一貫的風格,一步步的分析其執行流程,相信大家學完肯定能完全的掌握它的源碼結構。

ok,今天就先發佈這一篇吧。

各位如果還有哪裡不明白的,或是我這裡講的還不夠透徹,亦或是講錯了的地方請留言指正,讓我們共同進步,謝謝

同時,請大家掃一掃關註我的微信公眾號,雖然寫的不是很勤,但是每一篇都有質量保證,讓您學習到真正的知識。

關註我的微信公眾號


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、set數據結構 (使用 new來創建一個set集合 通過add方法添加元素 通過size來獲取set集合的長度) 還有另外一種初始化的方式(通過直接把數組傳遞進去) set裡面的元素不可以重覆(可以通過set的這個特性進行數組去重,註意:轉化過程中不會進行 數據類型轉化) set的一些方法(ad ...
  • 只要是說到js的變數和數據類型,就脫不開null和undefined,這兄弟倆就是js的重要基礎,不可不察,無數的同學們都用過放大鏡多角度多批次地研究過這兄弟倆,真是深受歡迎。^-^ js也真是怪異,有個null不夠,還要折騰出個undefined。 雖然被詬病,但這就是設計。 在c#和java中, ...
  • 驗證碼 ...
  • ——基於es6:Promise/A+ 規範簡單實現 非同步流程式控制制思想 前言: nodejs強大的非同步處理能力使得它在伺服器端大放異彩,基於它的應用不斷的增加,但是非同步隨之帶來的嵌套、難以理解的代碼讓nodejs看起來並沒有那麼優雅,顯得臃腫。類似於這樣的代碼: 上面的代碼 println中定義了va ...
  • 隨著Android的蓬勃發展, CPU的架構也越來越多. 早期只支持ARMv5, 截至目前, 支持的架構已達三類七種: ARM(ARMv5,ARMv7 (從2010年起),ARMv8), x86(x86 (從2011年起),x86_64 (從2014年起)) ,MIPS(MIPS (從2012年起) ...
  • 電池標稱電壓 定義如下圖, 以25度為例,20度再往上點, 4V 放一下電就往下掉, 3V 放一下電就往下掉, 假設 3.8V 是擁有最多 capacity 可以 discharge 的電壓,放電放了很久仍維持 3.8V, 因此我們定義 3.8V 為這顆電池的標稱電壓,(不同型號的電池也許不一樣) ...
  • Apple取消了oc的指針以及其他不安全的訪問的使用,捨棄的smalltalk語法,全面改為點語法,提供了類似java的命名空間 範型 重載; 首先我們瞭解一下Swift這門語言。Swift就像C語言一樣,使用變數來進行存儲並通過變數名來關聯值。在swift中廣泛的使用著不可變的變數,也就是我們常說 ...
  • Origin 一顆電池被拉載後,會產生電流及電壓如下圖, 如何計算其電池內阻呢 其公式為 R = |delta(V) / delta(I)| 公式推導如下: V1 = 10 I1R 左圖 V2 = 10 I2R 右圖 === 10 = V1 + I1R 10 = V2 + I2R === V1 + ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...