glide面試

Q1:看過Glide源碼嗎,你印象最深的是什么奠货?

Glide緩存簡介
Glide的緩存設(shè)計可以說是非常先進的壤玫,考慮的場景也很周全。在緩存這一功能上历筝,Glide又將它分成了兩個模塊,一個是內(nèi)存緩存誓斥,一個是硬盤緩存薪者。

這兩個緩存模塊的作用各不相同,內(nèi)存緩存的主要作用是防止應(yīng)用重復(fù)將圖片數(shù)據(jù)讀取到內(nèi)存當中玉吁,而硬盤緩存的主要作用是防止應(yīng)用重復(fù)從網(wǎng)絡(luò)或其他地方重復(fù)下載和讀取數(shù)據(jù)照弥。

內(nèi)存緩存和硬盤緩存的相互結(jié)合才構(gòu)成了Glide極佳的圖片緩存效果,那么接下來我們就分別來分析一下這兩種緩存的使用方法以及它們的實現(xiàn)原理诈茧。

Q2:簡單說一下Glide的三級緩存产喉?

相關(guān)文章:Glide源碼學(xué)習(xí)四:緩存

Android圖片加載框架最全解析(三),深入探究Glide的緩存機制

【Android - 進階】之圖片三級緩存的原理及實現(xiàn)

Android Glide緩存策略分析

簡單描述:

讀取的順序是:Lru算法緩存敢会、弱引用緩存曾沈、磁盤緩存

寫入的順序是:弱引用緩存、Lru算法緩存鸥昏、磁盤緩存(不準確)

下面敘述一下三級緩存的流程:(【Android - 進階】之圖片三級緩存的原理及實現(xiàn))

當我們的APP中想要加載某張圖片時塞俱,先去LruCache中尋找圖片,如果LruCache中有吏垮,則直接取出來使用障涯,如果LruCache中沒有,則去WeakReference中尋找膳汪,如果WeakReference中有唯蝶,則從WeakReference中取出圖片使用,同時將圖片重新放回到LruCache中遗嗽,如果WeakReference中也沒有圖片粘我,則去文件系統(tǒng)中尋找,如果有則取出來使用痹换,同時將圖片添加到LruCache中征字,如果沒有都弹,則連接網(wǎng)絡(luò)從網(wǎng)上下載圖片。圖片下載完成后匙姜,將圖片保存到文件系統(tǒng)中畅厢,然后放到LruCache中。

嚴格來講氮昧,并沒有什么Glide的三級緩存框杜,因為Glide的緩存只有兩個模塊,一個是內(nèi)存緩存郭计,一個是磁盤緩存霸琴。其中內(nèi)存緩存又分為Lru算法的緩存和弱引用緩存。

LruCache算法昭伸,Least Recently Used梧乘,又稱為近期最少使用算法。主要算法原理就是把最近所使用的對象的強引用存儲在LinkedHashMap上庐杨,并且选调,把最近最少使用的對象在緩存池達到預(yù)設(shè)值之前從內(nèi)存中移除。

public class LruCache<T, Y> {
    private final LinkedHashMap<T, Y> cache = new LinkedHashMap<T, Y>(100, 0.75f, true);
}
弱引用緩存:

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
    //弱引用緩存
    private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
    ...
    activeResources = new HashMap<Key, WeakReference<EngineResource<?>>>();
}

Glide緩存機制大致分為三層:Lru算法緩存灵份、弱引用緩存仁堪、磁盤緩存。

讀取的順序是:Lru算法緩存填渠、弱引用緩存弦聂、磁盤緩存

寫入的順序是:弱引用緩存、Lru算法緩存氛什、磁盤緩存(不準確)

我們先來看讀容汉:Lru算法緩存、弱引用緩存枪眉、磁盤緩存
首先 memoryCache的初始值是一個LruResourceCache對象捺檬,即默認是lru算法的緩存。

GlideBuilder.java
   private MemoryCache memoryCache;
   Glide  createGlide() {
       memoryCache = new LruResourceCache(calculator.getMemoryCacheSize())贸铜;
   }

Engine.java
   private final MemoryCache cache;
   public <T, Z, R> LoadStatus load(...){
       ...
       //獲取Lru算法的緩存堡纬,如果沒有,就從弱引用中獲取緩存
       EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
       ...
       //從弱引用中獲取緩存
       EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
       ...
       //從磁盤中獲取緩存
       EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
       DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, 
              fetcher, loadProvider, transformation,transcoder,             
              diskCacheProvider, diskCacheStrategy, priority);
   }

接下來我們看寫入:弱引用緩存蒿秦、Lru算法緩存烤镐、磁盤緩存
內(nèi)存緩存的寫入:
EngineResource是用一個acquired(int)變量用來記錄圖片被引用的次數(shù),調(diào)用acquire()方法會讓變量加1棍鳖,
調(diào)用release()方法會讓變量減1职车。
acquired變量大于0的時候,說明圖片正在使用中,也就應(yīng)該放到activeResources弱引用緩存當中悴灵;
如果acquired變量等于0了,說明圖片已經(jīng)不再被使用了骂蓖,那么此時會調(diào)用方法來釋放資源积瞒,
這里首先會將緩存圖片從activeResources中移除,然后再將它put到LruResourceCache當中登下。
這樣也就實現(xiàn)了正在使用中的圖片使用弱引用來進行緩存茫孔,不在使用中的圖片使用LruCache來進行緩存的功能

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
    ...    
 
    @Override
    public void onEngineJobComplete(Key key, EngineResource<?> resource) {
        Util.assertMainThread();
        // A null resource indicates that the load failed, usually due to an exception.
        if (resource != null) {
            resource.setResourceListener(key, this);
            if (resource.isCacheable()) {
                activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
            }
        }
        jobs.remove(key);
    }
}

現(xiàn)在就非常明顯了,可以看到被芳,在第13行缰贝,回調(diào)過來的EngineResource被put到了activeResources當中,也就是在這里寫入的緩存畔濒。

那么這只是弱引用緩存剩晴,還有另外一種LruCache緩存是在哪里寫入的呢?這就要介紹一下EngineResource中的一個引用機制了侵状。觀察剛才的handleResultOnMainThread()方法赞弥,在第15行和第19行有調(diào)用EngineResource的acquire()方法,在第23行有調(diào)用它的release()方法趣兄。其實绽左,EngineResource是用一個acquired變量用來記錄圖片被引用的次數(shù),調(diào)用acquire()方法會讓變量加1艇潭,調(diào)用release()方法會讓變量減1拼窥,代碼如下所示:

class EngineResource<Z> implements Resource<Z> {
 
    private int acquired;
    ...
 
    void acquire() {
        if (isRecycled) {
            throw new IllegalStateException("Cannot acquire a recycled resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
            throw new IllegalThreadStateException("Must call acquire on the main thread");
        }
        ++acquired;
    }
 
    void release() {
        if (acquired <= 0) {
            throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
            throw new IllegalThreadStateException("Must call release on the main thread");
        }
        if (--acquired == 0) {
            listener.onResourceReleased(key, this);
        }
    }
}

也就是說,當acquired變量大于0的時候蹋凝,說明圖片正在使用中鲁纠,也就應(yīng)該放到activeResources弱引用緩存當中。而經(jīng)過release()之后仙粱,如果acquired變量等于0了房交,說明圖片已經(jīng)不再被使用了,那么此時會在第24行調(diào)用listener的onResourceReleased()方法來釋放資源伐割,這個listener就是Engine對象候味,我們來看下它的onResourceReleased()方法:

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
 
    private final MemoryCache cache;
    private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
    ...    
 
    @Override
    public void onResourceReleased(Key cacheKey, EngineResource resource) {
        Util.assertMainThread();
        activeResources.remove(cacheKey);
        if (resource.isCacheable()) {
            cache.put(cacheKey, resource);
        } else {
            resourceRecycler.recycle(resource);
        }
    }
    ...
}

可以看到,這里首先會將緩存圖片從activeResources中移除隔心,然后再將它put到LruResourceCache當中白群。這樣也就實現(xiàn)了正在使用中的圖片使用弱引用來進行緩存,不在使用中的圖片使用LruCache來進行緩存的功能硬霍。

這就是Glide內(nèi)存緩存的實現(xiàn)原理帜慢。

Q3:Glide加載一個一兆的圖片(100100),是否會壓縮后再加載,放到一個200200的view上會怎樣粱玲,1000*1000呢躬柬,圖片會很模糊,怎么處理抽减?

而使用Glide允青,我們就完全不用擔心圖片內(nèi)存浪費,甚至是內(nèi)存溢出的問題卵沉。因為Glide從來都不會直接將圖片的完整尺寸全部加載到內(nèi)存中颠锉,而是用多少加載多少。Glide會自動判斷ImageView的大小史汗,然后只將這么大的圖片像素加載到內(nèi)存當中琼掠,幫助我們節(jié)省內(nèi)存開支。

ImageView默認的scaleType是FIT_CENTER

FitCenter的效果:會將圖片按照原始的長寬比充滿全屏

那么停撞,Glide將圖片壓縮到什么程度呢瓷蛙?.

關(guān)于圖片加載神器--Glide與Picasso的使用與比較

https://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

在默認情況下Picasso和Glide的外部緩存機制是非常不一樣的,通過實驗可以發(fā)現(xiàn)(1920x1080 像素的圖片被加載到768x432像素的imageview中)怜森,Glide緩存的是768x432像素的圖片速挑,而Picasso緩存的是整張圖片(1920x1080像素)。

當我們調(diào)整imageview的大小時副硅,Picasso會不管imageview大小是什么姥宝,總是直接緩存整張圖片,而Glide就不一樣了恐疲,它會為每個不同尺寸的Imageview緩存一張圖片腊满,也就是說不管你的這張圖片有沒有加載過,只要imageview的尺寸不一樣培己,那么Glide就會重新加載一次碳蛋,這時候,它會在加載的imageview之前從網(wǎng)絡(luò)上重新下載省咨,然后再緩存肃弟。

防止各位不明白,再來舉個例子零蓉,如果一個頁面的imageview是200200像素笤受,而另一個頁面中的imageview是100100像素,這時候想要讓兩個imageview像是同一張圖片敌蜂,那么Glide需要下載兩次圖片箩兽,并且緩存兩張圖片。

Q4:Glide 緩存原理章喉,如何設(shè)計一個大圖加載框架汗贫。

如何實現(xiàn)一個圖片加載框架

概括來說身坐,圖片加載包含封裝,解析落包,下載部蛇,解碼,變換咐蝇,緩存搪花,顯示等操作。

流程圖如下:


image.png

Q5:LRUCache 原理嘹害。

LRUCache源碼分析

LruCache算法,又稱為近期最少使用算法吮便。主要算法原理就是把最近所使用的對象的強引用存儲在LinkedHashMap上笔呀,并且,把最近最少使用的對象在緩存池達到預(yù)設(shè)值之前從內(nèi)存中移除髓需。

Q6:Glide VS Picasso

雙胞胎兄弟之間的對比许师,使用方式相同,但 Glide 之所以勝出僚匆,不僅僅是 Google的推薦微渠,更多應(yīng)該歸功于 GIF 的支持。 在沒有 Glide 之前咧擂,常用的做法就是寫了個自定義 view 然后 用一個 media 去播放逞盆。有了 Glide 之后幾乎對于 GIF 無感知了的, 內(nèi)部已經(jīng)支持了的松申≡坡可以像普通圖片那樣去加載并且顯示出來動圖。

安卓圖片顯示的質(zhì)量配置主要分為四種:

ARGB_8888 :32位圖,帶透明度,每個像素占4個字節(jié)
ARGB_4444 :16位圖,帶透明度,每個像素占2個字節(jié)
RGB_565 :16位圖,不帶透明度,每個像素占2個字節(jié)
ALPHA_8 :32位圖,只有透明度,不帶顏色,每個像素占4個字節(jié)
(A代表透明度,RGB代表紅綠藍:即顏色)

Picasso的默認質(zhì)量是 ARGB_8888
Glide的默認質(zhì)量則為 RGB_565

加載一張4000 * 2000(一般手機拍攝的都超過這個像素)的圖片

Picasso需要占用的內(nèi)存為: 32MB

4000 * 2000 * 4 / 1024 / 1024 = 30 (MB)

Glide需要占用的內(nèi)存為: 16MB

4000 * 2000 * 2 / 1024 / 1024 = 15 (MB)

也就是說只要同時加載幾張圖片,你的應(yīng)用就會OOM(內(nèi)存溢出了),最恐怖的是就算你的ImageView的寬高只有10px,同樣會占用那么多內(nèi)存,這就是為什么需要做圖片壓縮的原因了

Q7:Glide VS fresco

兩個都支持 GIF贸桶。所以 GIF 這一關(guān)pass掉舅逸。說到這里不得不提到一個頭疼的OOM問題,fresco 之所以很快闖入大家的視線皇筛,大概就是因為 Facebook 說他們使用了 native 內(nèi)存規(guī)避掉了 OutOfMemoryError 問題琉历。而且官方還專門寫了個demo,把幾大流行的開源庫都集成進去水醋,為了說明自己的圖片加載庫加載同樣的圖片速度更快旗笔,內(nèi)存占用更低。所以 fresco 相比較于 Glide 的(官方)優(yōu)勢就是這兩點: 內(nèi)存以及加載速度离例。但是我為什么依舊堅持拋棄了 fresco ?

“ In Android 4.x and lower, Fresco puts images in a special region of Android memory. This lets your application run faster - and suffer the dreaded OutOfMemoryError much less often.” 官方的原話是這么說的换团,所以在高版本上面依舊使用的Java 內(nèi)存,所以不可避免依舊會占用內(nèi)存宫蛆。
提到內(nèi)存艘包,不得不說到另外一個笑話的猛,fresco 最大只支持圖片文件大小為 2M 。記得有一次幫其他團隊跟蹤問題想虎,看到了 fresco 源碼中有一個 最大 size 2M 常量 卦尊。于是當場找了一個10M的圖片作為測試。 Glide 正常顯示舌厨, fresco顯示黑屏岂却。。裙椭。
使用方式上躏哩,fresco 推薦的是用他提供的 SimpleDraweeView . 這個方式意味著我們的遷移成本會非常的高,要改布局文件揉燃,其次還必須給定大猩ǔ摺(或者比例)。 當然他也支持代碼來加載圖片炊汤,比如 DraweeHierarchy正驻,但是寫起來還是真心很費勁的,很不友好抢腐,改動成本居高姑曙。
fresco 更多是native實現(xiàn)。所以需要對NDK有所了解迈倍,但個人對NDK不太了解伤靠,相比較于 Glide, 同樣遇到問題之后授瘦,修改源碼的成本醋界,Glide 成本更可控。前者可能就不太好下手了的提完。
Glide 各種 BitmapTransformation形纺,比如圓形,圓角等徒欣,更讓人喜歡逐样。
這一點就當隨意吐槽一下,當然也可以說心疼一下 Facebook打肝。因為在沒有 Android studio (gradle構(gòu)建)的情況下脂新,想必大家都用的是 eclipse 吧。那么就意味著 fresco 得提供 Jar 包. 這一點當時也是把很多人拒之門外了的粗梭,可笑的是當 Facebook 費了老大勁的搞出來 jar 包之后争便,大家早就紛紛轉(zhuǎn)戰(zhàn) gradle 構(gòu)建工程, 直接 maven 依賴啦。大寫的尷尬断医。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滞乙,一起剝皮案震驚了整個濱河市奏纪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斩启,老刑警劉巖序调,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異兔簇,居然都是意外死亡发绢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門垄琐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來边酒,“玉大人,你說我怎么就攤上這事狸窘∩醺伲” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵朦前,是天一觀的道長。 經(jīng)常有香客問我鹃操,道長韭寸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任荆隘,我火速辦了婚禮恩伺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘椰拒。我一直安慰自己晶渠,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布燃观。 她就那樣靜靜地躺著褒脯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缆毁。 梳的紋絲不亂的頭發(fā)上番川,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音脊框,去河邊找鬼颁督。 笑死,一個胖子當著我的面吹牛浇雹,可吹牛的內(nèi)容都是我干的沉御。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼昭灵,長吁一口氣:“原來是場噩夢啊……” “哼吠裆!你這毒婦竟也來了伐谈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤硫痰,失蹤者是張志新(化名)和其女友劉穎衩婚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體效斑,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡非春,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缓屠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奇昙。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖敌完,靈堂內(nèi)的尸體忽然破棺而出储耐,到底是詐尸還是另有隱情,我是刑警寧澤滨溉,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布什湘,位于F島的核電站,受9級特大地震影響晦攒,放射性物質(zhì)發(fā)生泄漏闽撤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一脯颜、第九天 我趴在偏房一處隱蔽的房頂上張望哟旗。 院中可真熱鬧,春花似錦栋操、人聲如沸闸餐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舍沙。三九已至,卻和暖如春剔宪,著一層夾襖步出監(jiān)牢的瞬間场勤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工歼跟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留和媳,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓哈街,卻偏偏與公主長得像留瞳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子骚秦,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

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