成為一名優秀的Android開發,需要一份完備的[知識體系](https://github.com/JsonChao/Awesome-Android-Exercise),在這里,讓我們一起成長為自己所想的那樣~。
眾所周知,**優秀源碼的閱讀與理解是最能提升自身功力的途徑**,如果想要成為一名優秀的Android工程師,那么**Android中優秀三方庫源碼的分析和理解則是必備技能**。就拿比較熱門的圖片加載框架Glide來說,相信很多同學都使用過,那么,當別人問你下面這些問題時你是否能回答出來呢?(Glide五連發)
* 1、為什么要在項目中使用這個庫?
* 2、這個庫都有哪些用法?對應什么樣的使用場景?
* 3、這個庫的核心實現原理是什么?如果讓你實現這個庫的某些核心功能,你會考慮怎么去實現?
* 4、Glide源碼機制的核心思想是什么?
* 5、Glide中是如何計算一張圖片的大小的?
相信能全部回答出來的同學并不多,下面我來解答一下上面幾個問題。
### 1、為什么要在項目中使用這個庫?
* 1、多樣化媒體加載:不僅可以進行圖片緩存,還支持Gif、WebP、縮略圖,甚至是Video。
* 2、通過設置綁定生命周期:可以使加載圖片的生命周期動態管理起來。
* 3、高效的緩存策略:支持內存、Disk緩存,并且Picasso只會緩存原始尺寸的圖片,而Glide緩存的是多種規格,也就是Glide會根據你ImageView的大小來緩存相應大小的圖片尺寸。
* 4、內存開銷小:我們可以手動配置默認的Bitmap解碼格式為 RGB\_565格式,以減少圖片占用的內存開銷。
### 2、這個庫都有哪些用法?對應什么樣的使用場景?
* 1、圖片加載:Glide.with(this).load(imageUrl).override(800, 800).placeholder().error().animate().into()。
* 2、多樣式媒體加載:asBitamp、asGif。
* 3、生命周期集成。
* 4、可以配置磁盤緩存策略ALL、NONE、SOURCE、RESULT。
### 3、這個庫的核心實現原理是什么?如果讓你實現這個庫的某些核心功能,你會考慮怎么去實現?
要想了解Glide的核心實現原理,就必須先從它的加載API Glide.with().into()來進行分析。
#### 1、Glide&with:
* 1、初始化各式各樣的配置信息(包括緩存,請求線程池,大小,圖片格式等等)以及glide對象。
* 2、將glide請求和application/SupportFragment/Fragment的生命周期綁定在一塊。
#### 2、Glide&load:
設置請求url,并記錄url已設置的狀態。
#### 3、Glide&into:
* 1、首先根據轉碼類transcodeClass類型返回不同的ImageViewTarget:BitmapImageViewTarget、DrawableImageViewTarget。
* 2、遞歸建立縮略圖請求,沒有縮略圖請求,則直接進行正常請求。
* 3、如果沒指定寬高,會根據ImageView的寬高計算出圖片寬高,最終執行到onSizeReay()方法中的engine.load()方法。
* 4、engine是一個負責加載和管理緩存資源的類
其中Glide的三層緩存機制是值得我們去反復學習揣摩的,這里我們先了解下常規的三級緩存是怎樣的。
#### 常規三級緩存的流程:強引用->軟引用->硬盤緩存
當我們的APP中想要加載某張圖片時,先去LruCache中尋找圖片,如果LruCache中有,則直接取出來使用,如果LruCache中沒有,則去SoftReference中尋找(軟引用適合當cache,當內存吃緊的時候才會被回收。而weakReference在每次system.gc()就會被回收)(當LruCache存儲緊張時,會把最近最少使用的數據放到SoftReference中),如果SoftReference中有,則從SoftReference中取出圖片使用,同時將圖片重新放回到LruCache中,如果SoftReference中也沒有圖片,則去硬盤緩存中中尋找,如果有則取出來使用,同時將圖片添加到LruCache中,如果沒有,則連接網絡從網上下載圖片。圖片下載完成后,將圖片保存到硬盤緩存中,然后放到LruCache中。
#### Glide的三層緩存機制
Glide緩存機制大致分為三層:弱引用緩存、內存緩存、磁盤緩存。
* 取的順序是:弱引用、內存、磁盤。
* 存的順序是:弱引用、內存、磁盤。
三層存儲的機制在Engine中實現的。先說下Engine是什么?Engine這一層負責加載時做管理內存緩存的邏輯。持有MemoryCache、Map>>。通過load()來加載圖片,加載前后會做內存存儲的邏輯。如果內存緩存中沒有,那么才會使用EngineJob這一層來進行異步獲取硬盤資源或網絡資源。EngineJob類似一個異步線程或observable。Engine是一個全局唯一的,通過Glide.getEngine()來獲取。
需要一個圖片資源,如果Lrucache中有相應的資源圖片,那么就返回,同時從Lrucache中清除,放到activeResources中。activeResources map是盛放正在使用的資源,以弱引用的形式存在。同時資源內部有被引用的記錄。如果資源沒有引用記錄了,那么再放回Lrucache中,同時從activeResources中清除。如果Lrucache中沒有,就從activeResources中找,找到后相應資源引用加1。如果Lrucache和activeResources中沒有,那么進行資源異步請求(網絡/diskLrucache),請求成功后,資源放到diskLrucache和activeResources中。
### 4、Glide源碼機制的核心思想:
使用一個弱引用map activeResources來盛放項目中正在使用的資源。Lrucache中不含有正在使用的資源。資源內部有個計數器來顯示自己是不是還有被引用的情況,把正在使用的資源和沒有被使用的資源分開有什么好處呢??因為當Lrucache需要移除一個緩存時,會調用resource.recycle()方法。注意到該方法上面注釋寫著只有沒有任何consumer引用該資源的時候才可以調用這個方法。那么為什么調用resource.recycle()方法需要保證該資源沒有任何consumer引用呢?glide中resource定義的recycle()要做的事情是把這個不用的資源(假設是bitmap或drawable)放到bitmapPool中。bitmapPool是一個bitmap回收再利用的庫,在做transform的時候會從這個bitmapPool中拿一個bitmap進行再利用。這樣就避免了重新創建bitmap,減少了內存的開支。而既然bitmapPool中的bitmap會被重復利用,那么肯定要保證回收該資源的時候(即調用資源的recycle()時),要保證該資源真的沒有外界引用了。這也是為什么glide花費那么多邏輯來保證Lrucache中的資源沒有外界引用的原因。
### 5、Glide中是如何計算一張圖片的大小的?
圖片占用內存的計算公式:圖片高度 \* 圖片寬度 \* 一個像素占用的內存大小。所以,計算圖片占用內存大小的時候,要考慮圖片所在的目錄跟設備密度,這兩個因素其實影響的是圖片的寬高,android會對圖片進行拉升跟壓縮。
上面筆者只是簡單地講解一下下Glide的內部實現機制,但是這是遠遠不夠的,如果想要對Glide或其它熱門三方庫有足夠**具象**地了解,就**必須深入源碼去感受其中的藝術**。
## 助力一份Android熱門三方庫源碼面試寶典
因此,為了將熱門三方庫涉及的知識**成體系**地融合起來,筆者創建了Awesome-Third-Library-Source-Analysis這個項目,為的就是讓每一個Android工程師能夠從以下**七個方面全方位地提升自己的技術實力**。
[項目地址:Awesome-Third-Library-Source-Analysis](https://github.com/JsonChao/Awesome-Third-Library-Source-Analysis)

> 深入理解熱門三方庫實現原理,從七個角度全方位提升你的功力~
## Contents
### 網絡
* [OkHttp](https://juejin.im/post/5e1be39b6fb9a02fcd130d1f) (已完成)
Android最優秀的網絡底層框架,沒有之一。
* [Retrofit](https://juejin.im/post/5e1fb9386fb9a0300a4501a6) (已完成)
Android最優秀的網絡封裝框架,內含九種常用設計模式的靈活運用。
### 圖片
* [Glide](https://juejin.im/post/5e2109e25188254c257c40c6) (已完成)
Android使用最廣泛的圖片加載框架。
### 數據庫
* [GreenDao](https://juejin.im/post/5e44b3c2e51d4526ec0d2b71) (已完成)
Android中數據庫操作綜合效率最高的框架。
### 響應式編程
* [RxJava](https://juejin.im/post/5e4c9d45518825496e7847b1) (已完成)
來一起探究RxJava的異步、簡潔、優雅和它強大的操作符吧!
### 內存泄露
* [LeakCanary](https://juejin.im/post/5e5330f8e51d4526d43f30ef) (已完成)
LeakCanary究竟是如何檢測出內存泄露的呢?
### 依賴注入
* [ButterKnife](https://juejin.im/post/5e55d38d518825491753ae39)(已完成)
使用APT + 注解攻破了findViewByid(),JW大神之作。
* [Dagger2](https://juejin.im/post/5e58779f518825493f6ce7eb)(已完成)
Dagger就一把匕首,在中大型項目中,它能提升開發效率、自動管理類的實例、解耦,是如此的干脆。
### 事件總線
* [EventBus](https://juejin.im/post/5e61b253e51d45270e212eb4)(已完成)
使用擴展的觀察者模式實現的組件間通信框架,廣播的替代者。
鏈接:https://juejin.im/post/5e65ad276fb9a07cc01a3264