1. Android Glide4.0+圖片載入進度監聽
在近期使用Glide4.0+版本的時候,需要進行圖片載入進度的監聽,於是查找各種資料實現該功能,便有了這篇記錄。
筆者Glide為:
大致思路:通過Okhttp的攔截器,監聽圖片Url的載入進度(需要自己實現邏輯計算),並回調!
1,步驟1,將 OkHttpUrlLoader 添加到項目:
2,步驟2,將 OkHttpStreamFetcher 添加到項目:
3,步驟3,自定義攔截器和回調介面:
4,步驟4,計算載入進度,並在自定義的攔截器中使用:
5,在Glide中啟用:
本文僅為記錄,詳細分析參考: 郭霖大神Glide系列文章
2. Android 【手撕Glide】--Glide緩存機制(面試)
本文源碼解析基於Glide 4.6.1
系列文章
Android 【手撕Glide】--Glide緩存機制
Android 【手撕Glide】--Glide緩存機制(面試)
Android 【手撕Glide】--Glide是如何關聯生命周期的?
Glide緩存分為內存緩存和磁碟緩存,其中內存緩存是由弱引用+LruCache組成。
取的順序是:弱引用、LruCache、磁碟
存的順序是:磁碟、弱引用、LruCache
這張親手製作的圖片,方便大家更直觀的理解緩存機制的整體流程,結合文末總結效果更佳。喜歡的記得點贊!
概述
1、弱引用是由這樣一個HashMap維護,key是緩存的key,這個key由圖片url、width、height等10來個參數組成;value是圖片資源對象的弱引用形式。
2、LruCache是由一個LinkedHashMap維護,根據Lru演算法來管理圖片。大致的原理是利用linkHashMap鏈表的特性,把最近使用過的文件插入到列表頭部,沒使用的圖片放在尾部;然後當圖片大小到達預先設置的一個閥值的時候 ,按演算法刪除列表尾部的部分數據。由於篇幅有限,這里不講解LruCache和DiskLruCache的底層原理,這里推薦一篇 圖解LinkedHashMap原理
這是Glide自定義的LruCache
存取原理
取數據
在內存緩存中有一個概念叫圖片引用計數器 ,具體來說是在 EngineResource 中定義一個 acquired 變數用來記錄圖片被引用的次數,調用 acquire() 方法會讓變數加1,調用 release() 方法會讓變數減1。
獲取圖片資源是先從弱引用取緩存,拿到的話,引用計數+1;沒有的話從LruCache中拿緩存,拿到的話,引用計數也是+1,同時把圖片從LruCache緩存轉移到弱應用緩存池中;再沒有的話就通過 EngineJob 開啟線程池去載入圖片,拿到的話,引用計數也是+1,會把圖片放到弱引用。
存數據
很明顯,這是載入圖片之後的事情。通過 EngineJob 開啟線程池去載入圖片,取到數據之後,會回調到主線程,把圖片存到弱引用。當圖片不再使用的時候,比如說暫停請求或者載入完畢或者清除資源時,就會將其從弱引用中轉移到 LruCache 緩存池中。 總結一下,就是正在使用中的圖片使用 弱引用 來進行緩存,暫時不用的圖片使用 LruCache 來進行緩存的功能;同一張圖片只會出現在 弱引用 和 LruCache 中的一個。
為什麼要引入軟引用?
1、分壓策略,減少Lrucache 中 trimToSize 的概率。如果正在remove的是張大圖,lrucache正好處在臨界點,此時remove操作,將延緩Lrucache的 trimToSize 操作;
2 提高效率:弱引用用的是 HashMap ,Lrucache用的是 LinkedHashMap ,從訪問效率而言,肯定是 HashMap 更高。
Glide磁碟緩存策略(4.x)
如果在內存緩存中沒獲取到數據會通過 EngineJob 開啟線程池去載入圖片,這里有2個關鍵類: DecodeJob 和 EngineJob 。 EngineJob 內部維護了線程池,用來管理資源載入,當資源載入完畢的時候通知回調; DecodeJob 是線程池中的一個任務。
磁碟緩存是通過 DiskLruCache 來管理的,根據緩存策略,會有2種類型的圖片, DATA (原始圖片)和 RESOURCE (轉換後的圖片)。磁碟緩存依次通過 ResourcesCacheGenerator 、 SourceGenerator 、 DataCacheGenerator 來獲取緩存數據。 ResourcesCacheGenerator 獲取的是轉換過的緩存數據; SourceGenerator 獲取的是未經轉換的原始的緩存數據; DataCacheGenerator 是通過網路獲取圖片數據再按照按照緩存策略的不同去緩存不同的圖片到磁碟上。
Glide緩存分為 弱引用+ LruCache+ DiskLruCache ,其中讀取數據的順序是:弱引用 > LruCache > DiskLruCache>網路;寫入緩存的順序是:網路 --> DiskLruCache--> LruCache-->弱引用
內存緩存分為弱引用的和 LruCache ,其中正在使用的圖片使用弱引用緩存,暫時不使用的圖片用 LruCache緩存,這一點是通過 圖片引用計數器(acquired變數)來實現的,詳情可以看內存緩存的小結。
磁碟緩存就是通過DiskLruCache實現的,根據緩存策略的不同會獲取到不同類型的緩存圖片。它的邏輯是:先從轉換後的緩存中取;沒有的話再從原始的(沒有轉換過的)緩存中拿數據;再沒有的話就從網路載入圖片數據,獲取到數據之後,再依次緩存到磁碟和弱引用。
參考:
面試官:簡歷上最好不要寫Glide,不是問源碼那麼簡單
原來面試的時候寫精通Glide,這樣問我這樣答
3. Android之 glide 框架 解讀
glide圖片載入框架 其目的幫助我們在開發過程中對控制項上的圖片載入減壓,它能夠通過參數和方法載入 本地圖片 網路圖片 二進制等到控制項上 還能夠設置預載入以及載入出錯時的錯誤提示圖片,還能夠對載入的圖片進行緩存重復利用 可以緩存原始照片還能緩存經過壓縮處理後的照片,能夠緩存到內存,或者硬碟,甚至glide可以載入GIF動圖還可以給載入的靜態圖片指定大小,對於 glide的緩存機制它用的是lru 這種緩存機制 其實就是 在一定緩存空間內把最近用的以及重復用的排列在最高級 將最不常用沒有重復使用的排在最低級 當有新實例出現,緩存空間不夠用的情況下就會把被打有最低級標志的實例釋放掉。
4. Android:深入剖析圖片載入庫Glide緩存功能(源碼分析)
Glide 需要緩存的 圖片資源 分為兩類:
Glide 的緩存機制使得 Glide 具備非常好的圖片緩存效果,從而使得具備較高的圖片載入效率。
下面,我將根據 Glide 緩存流程中的每個步驟 進行源碼分析。
至此, Glide 的圖片緩存 Key 生成完畢。
至此,創建好了緩存對象 LruResourceCache
即:
源碼分析如下:
若上述兩個方法都沒獲取到緩存圖片時(即內存緩存里沒有該圖片的緩存),就開啟新線程載入圖片。
若無法從 內存緩存 里 獲得緩存的圖片, Glide 就會採用第2級緩存:磁碟緩存 去獲取緩存圖片
寫入 內存緩存分為:寫入 弱引用緩存 & LruCache 演算法的緩存
寫入 LruCache 演算法 內存緩存的原理:包含圖片資源 resource 的 EngineResource 對象的一個引用機制:
所以:
至此,實現了:
至此, Glide 的圖片緩存流程解析完畢。
Android圖片載入的那些事:為什麼你的Glide 緩存沒有起作用?
不定期分享關於 安卓開發 的干貨,追求 短、平、快 ,但 卻不缺深度 。
5. Android知識點——Glide獲取圖片寬高
先很負責任的說一下,這個內容也是網路來的,但是很不負責任的是,當初只記錄解決方案,忘了記錄是查看的哪篇博客了,這里先對不知道借鑒的誰表示感謝。無法分享鏈接,就厚著臉皮把(轉)字去掉了,請大家諒解。
先提供一下 Android知識點——目錄 的鏈接,然後讓我們進入正題。
實際上,這篇博客所說的內容並不是所有人都可以用到,畢竟大多數時候,我們只需要展示圖片,而並不需要知道圖片的寬高;有的時候我們只需要知道展示的寬高(即ImageView)的寬高,不需要知道圖片資源的實際尺寸。
但是需求千千萬萬嘛,以程序員的腦洞,怎麼能想到產品的腦洞究竟有多大呢?我這里就遇到了一個需求,那就是需要在一個可縮放的圖片上標注icon(類似地圖上的marker)。這還不算完,畢竟在找到的圖片縮放控制項 PhotoView 中,我們點擊到圖片上後,是有點擊點位在整個圖片上的百分比坐標回調的。而多端通過百分比是很容易就能在圖片中獲取到相同的點位,並回顯出對應的icon的(沒辦法,誰讓我找的是方便計算百分比的呢),結果Web端優先做了這部分功能,使用的是在原圖上的具體坐標。這樣我百分比的計劃自然就落空了,只能想辦法計算出具體的點位。
因此獲取圖片的原始尺寸就是一個必不可少的環節,我剛剛網路了一下,查到 wangke_king 的 Android獲取圖片的寬度和高度 中使用的方法是:
我這里沒有親測過,不過應該是沒有問題,但是很遺憾我們的需求是在網路圖片上做測量,所以這個方法也無法使用,不過如果其他有類似本地圖片需求的,不妨嘗試一下。而我之前找到的解決方案為:
首先說明,上述的方法是可以實現圖片的尺寸測量的,只是有一個小小的問題,那就是想要計算出Drawable的寬高,需要必須等到圖片載入完成之後,嘗試了使用view.post(),監聽組件載入完成,但是並不是每次都能獲取到Drawable的寬高,因此當初的解決方案是寫了個兩秒鍾的定時器,每50毫秒測量一次,直到獲取到值為止。這樣的解決方案可謂是相當無腦了,而且還要消耗很多不必要的資源。
還好皇天不負有心人啊,終於找到了通過Glide獲取圖片寬高的方式:
這樣我們就可以通過回調,在Glide將網路圖片注入到對應的圖片組件的時候,得到圖片的Bitmap,然後在通過Bitmap來獲取圖片的寬高。但是需要注意的一點是,Bitmap的泛型是需要手動去設置的哦。
另外SimpleTarget現在已經過時,暫時還沒有查到。我搜索過SimpleTarget過時使用什麼替換,有一些說法是使用BitmapImageViewTarget ,不過下面是實際測試結果。
圖片鏈接:
網路圖片-景色
測量結果:
圖片信息:
如果不是我使用有誤的話,BitmapImageViewTarget 是無法替換SimpleTarget ,實現測量圖片原始寬高的功能的。
6. Android Glide(二)生命周期感知
主要分為三個層次的生命周期:Activity & 網路 & 內存。
我們一般認為,應該及時取消不必要的載入請求,這很耗費資源,但在Glide這並不是必須的操作。因為 Glide 會在頁面生命周期 / 網路變化時,自動取消載入或重新載入。沒錯,就是這么牛逼。
還記得Glide使用的第一句嗎,Glide.with(參數),
with()方法可以接收Context、Activity或者Fragment類型的參數。
先說結論,根據傳入的參數不同,將對應於 Application & Activity & Fragment 的作用域,具體如下:
這里不細節分析源碼實現,我們只需要知道,不同參數通過重載方法,作用於不同的作用域
這里具體分析一下上面這些晦澀的語句,注意設計的思路重點,就是這個SupportRequestManagerFragment,這個隱藏SupportRequestManagerFragment,這個並不可見的SupportRequestManagerFragment。
Glide並沒有辦法知道Activity的生命周期,於是Glide就使用了添加隱藏Fragment的這種小技巧,相當於通過這個隱藏的fragment與對應的Activity建立了聯系,因為Fragment的生命周期和Activity是同步的,如果Activity被銷毀了,Fragment是可以監聽到的,這樣Glide就可以捕獲這個事件並停止圖片載入了。
這是 為了避免 SupportRequestManagerFragment 在一個作用域中重復創建。
因為commitAllowingStateLoss()是將事務 post 到消息隊列中的,也就是說,事務是非同步處理的,而不是同步處理的。假設沒有臨時保存記錄,一旦在事務非同步等待執行時調用了Glide.with(...),就會在該作用域中重復創建 Fragment。
從上面的分析我們得知,Glide 為每個Activity 和 Fragment 作用域創建了一個無界面的 Fragment,這里我們就來分析 Glide 如何監聽這個無界面 Fragment 的生命周期。
首先先提到一個概念,Lifecycle,大家可以粗略的把它理解為 移動APP頁面的狀態
其實說起來也是三步型宴陪走戰略,
1、在創建Fragment的時候會創建ActivityFragmentLifecycle對象;
2、在Fragment生命周期的方法中會調用Lifecycle的相關方法來通知RequestManager;
3、LifecycleListener 是一個介面,RequestManager實現了這個介面,Lifecycle最終是調用了lifecycleListener來通知相關的實現類的,也就是RequestManager。
這個就非常簡單了,也就是上述的RequestManager實現了LifecycleListener介面後,在對應的方法中,作出相應的處理。
主要關注以下幾點:
這個邏輯很簡單,在剛才所說的RequestManager的構造器中,會構建一個ConnectivityMonitor對象,它的默認構造工廠是,如果有網路監聽許可權,
則實例化DefaultConnectivityMonitor,在onStart()時注冊廣播監卜蠢聽器,而在onStop()時注銷廣播監聽器。在RequestManager中根據網路狀態進行相應的操作。
聽起祥睜來有點拗口,簡而言之,如果應用有監控網路狀態的許可權,那麼 Glide 會監聽網路連接狀態,在頁面可見時注冊廣播監聽器,而在頁面不可見時注銷廣播監聽器,並在網路重新連接時重新啟動失敗的請求。
這個的實現也非常簡單的的,在構建 Glide 時,會調用registerComponentCallbacks()進行全局注冊, 系統在內存緊張的時候回調onTrimMemory(level)。
而 Glide 則根據系統內存緊張級別(level)進行相應的回收,而 RequestManager 在 TRIM_MEMORY_MODERATE 級別會暫停請求。