上一篇博文寫的是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,今天就先發佈這一篇吧。
各位如果還有哪裡不明白的,或是我這裡講的還不夠透徹,亦或是講錯了的地方請留言指正,讓我們共同進步,謝謝
同時,請大家掃一掃關註我的微信公眾號,雖然寫的不是很勤,但是每一篇都有質量保證,讓您學習到真正的知識。