Android:簡(jiǎn)單實(shí)現(xiàn)并理解圖片三級(jí)緩存

學(xué)習(xí)Android網(wǎng)絡(luò)開發(fā)的過程中,勢(shì)必會(huì)經(jīng)歷很多痛苦的過程,其中一個(gè)大坑就是圖片緩存,當(dāng)然現(xiàn)在有很多現(xiàn)成的庫(kù)非常方便,常常幾行代碼就可以實(shí)現(xiàn)想要的功能,但不懂其中的原理是不行的,所以對(duì)于剛開始學(xué)習(xí)網(wǎng)絡(luò)編程的小猿們,最好的方法就是手動(dòng)實(shí)現(xiàn)一下。沒有經(jīng)歷過HttpClient或HttpUrlConnection連接網(wǎng)絡(luò)的繁瑣過程,怎么能感受到OkHttp,Volley,Retrofit的方便,下面,我們就一起開始學(xué)習(xí)圖片三級(jí)緩存。

使用圖片緩存的原因

  • 提高用戶體驗(yàn):如果每次啟動(dòng)都從網(wǎng)絡(luò)下載圖片,勢(shì)必會(huì)加載很慢,圖片無法顯示,或需要很久才能完全顯示,用戶體驗(yàn)及其不好
  • 節(jié)約流量:如果每次加載頁面,甚至只是滑動(dòng)控件瀏覽就會(huì)下載的話,會(huì)消耗很多流量,占用網(wǎng)絡(luò)資源的同時(shí),也會(huì)因?yàn)閼?yīng)用耗流量而用戶數(shù)量級(jí)受到影響

什么是三級(jí)緩存

  1. 內(nèi)存緩存:優(yōu)先加載,速度最快
  2. 本地緩存:次優(yōu)先加載,速度較快
  3. 網(wǎng)絡(luò)緩存:最后加載,速度較慢

緩存策略

為什么使用緩存策略

上面從用戶角度考慮了為什么要使用圖片緩存,此外,從開發(fā)人員角度看,Bitmap的創(chuàng)建非常消耗時(shí)間和內(nèi)存,可能導(dǎo)致頻繁GC,使用緩存策略能夠高效加載Bitmap,減少卡頓,從而減少讀取耗時(shí)和電量消耗。

緩存策略是什么

具體通過三級(jí)級(jí)緩存策略,內(nèi)存作為一級(jí)緩存,本地作為二級(jí)緩存,網(wǎng)絡(luò)直接下載為最后,其實(shí)嚴(yán)格來說不算緩存。其中內(nèi)存采用LruCache,其內(nèi)部通過LinkedhashMap來持有外界緩存對(duì)象的強(qiáng)引用;對(duì)于本地緩存,我這里為了簡(jiǎn)單快速理解原理,直接使用的是文件IO操作,而網(wǎng)上也有人采用DiskLruCache (不是Android官網(wǎng)提供,但被官網(wǎng)推薦)。加載圖片時(shí),首先采用LRU方式進(jìn)行尋找,若找不到指定內(nèi)容,則進(jìn)行本地搜索,若本地也找不到,向網(wǎng)絡(luò)發(fā)起請(qǐng)求來獲取圖片。

圖片請(qǐng)求緩存框架

調(diào)用bindBitmap,傳遞url和ImageView,首先是loadBitmapFromMemCache從內(nèi)存緩沖區(qū)中根據(jù)url去找,如果找不到的話,則調(diào)用loadBitmap,通過一個(gè)runnable,提交到線程池,得到結(jié)果后,通過主線程handler來進(jìn)行ui的更新。這里要注意,當(dāng)我們加載圖片的時(shí)候,如果圖片從內(nèi)存中找不到,調(diào)用了loadBitmap,這個(gè)時(shí)候,已經(jīng)將loadbitmap封裝在Runnable中,然后提交到線程池中了,也就是后續(xù)的都是和主線程處于異步的狀態(tài)同時(shí)展開的了,可以很好的解決因?yàn)锽itmap的申請(qǐng)創(chuàng)建耗時(shí)導(dǎo)致的掉幀現(xiàn)象的出現(xiàn)。

下面是loadBitmap的工作流程:



上圖中,對(duì)于loadBitmapFromHttp,在調(diào)用的時(shí)候,通過urlConnection,將網(wǎng)絡(luò)的數(shù)據(jù)流寫入到本地后,然后又調(diào)用的laodBitmapFromDiskCache,也就是在圖片被下載下來之后,首先會(huì)將其添加到我們的本地磁盤緩存中,然后當(dāng)這個(gè)圖片被使用的時(shí)候,我們又會(huì)將其添加到我們的內(nèi)存緩存中。

以上就是整個(gè)圖片請(qǐng)求緩存框架的流程介紹,基本都是按照函數(shù)方法的設(shè)計(jì)進(jìn)行介紹的,下面就來看看在代碼中,應(yīng)該如何實(shí)現(xiàn)。

圖片緩存代碼實(shí)現(xiàn)

首先是整個(gè)緩存的函數(shù),相當(dāng)于前面圖中的 bindBitmap,根據(jù)Url來獲取圖片Bitmap。


1、從內(nèi)存中獲取,函數(shù):loadBitmapFromMemCache,這里用到LruCache,是Android提供的一個(gè)緩存工具類,其算法是最近最少使用算法。它把最近使用的對(duì)象用“強(qiáng)引用”存儲(chǔ)在LinkedHashMap中,并且把最近最少使用的對(duì)象在緩存值達(dá)到預(yù)設(shè)定值之前就從內(nèi)存中移除。

注意,這里是將url作為key值進(jìn)行哈希,因?yàn)閡rl中可能有特殊字符影響使用,一般采用其MD5值來作為key,我這里沒有實(shí)現(xiàn),只是簡(jiǎn)單的將特殊符號(hào)進(jìn)行了替換。

2、從磁盤中加載,函數(shù):loadBitmapFromDiskCache

這里我用了自己寫的工具類FileUtils來進(jìn)行文件的讀寫,主要包括sd卡的檢查,讀取圖片,存取圖片等操作。注意,若從文件中獲取成功,則將其按照鍵值對(duì)的形式存至內(nèi)存中。

3、從網(wǎng)絡(luò)中獲取圖片,函數(shù):loadBitmapFromHttp

同理,這里用了工具類HttpUtils來進(jìn)行網(wǎng)絡(luò)的連接,獲取輸入流InputStream,同時(shí)將流直接BitmapFactory.decodeStream轉(zhuǎn)為Bitmap。若從網(wǎng)絡(luò)獲取圖片成功,要將圖片存入磁盤緩存,同時(shí)寫入內(nèi)存。

至此,一個(gè)簡(jiǎn)易的圖片緩存框架就結(jié)束了,但還要注意一下幾點(diǎn):

  • Bitmap縮放:從網(wǎng)絡(luò)加載過來的圖片我們不可能將其全部加載到內(nèi)存,需要根據(jù)其大小做一個(gè)顯示的處理,獲取圖片的寬高,根據(jù)控件的寬高進(jìn)行一個(gè)縮放,用BitmapFactory.Options修改其屬性后,設(shè)置為該Bitmap的屬性,具體代碼比較容易實(shí)現(xiàn),網(wǎng)上教程較多,這里不再贅述。
  • AsyncTask線程池問題:這里在實(shí)現(xiàn)加載圖片時(shí)大多是在多線程中進(jìn)行的,而android中比較常用比較方便的就是AsyncTask,我按照上述代碼完成后,發(fā)現(xiàn)程序加載圖片速度沒有明顯改進(jìn),依然會(huì)有一個(gè)肉眼可見的緩慢過程,經(jīng)過閱讀源碼發(fā)現(xiàn),雖然利用AsyncTask表面開啟了多個(gè)線程,但實(shí)際其底層只開了一個(gè)單線程順序執(zhí)行,因而想要同時(shí)開啟多個(gè)線程下載需要用線程池的方式開啟AsyncTask,具體原因見我上一篇博客
  • 創(chuàng)建類ImageLoader,將imageview,bitmap和url綁定在一起,同時(shí)給imageview設(shè)置tag進(jìn)行標(biāo)記,防止因?yàn)楫惒较螺d導(dǎo)致的圖片錯(cuò)位
  • 附上代碼用到的工具類代碼
    1、Http工具類

2、File工具類

參考資料

Android網(wǎng)絡(luò)圖片請(qǐng)求+二級(jí)緩存實(shí)現(xiàn)
Android ImageLoader 實(shí)現(xiàn)
Android中圖片的三級(jí)緩存
詳細(xì)解讀LruCache類

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,007評(píng)論 25 709
  • 一盞燈, 一片昏黃; 一簡(jiǎn)書, 一杯淡茶。 守著那一份淡定, 品讀屬于自己的寂寞。 保持淡定, 才能欣賞到最美麗的...
    這個(gè)碼農(nóng)不太牛閱讀 737評(píng)論 0 14
  • Java中的String類可以被繼承么? 答:不能,因?yàn)樗且粋€(gè)final類,同樣的還有Integer,F(xiàn)loat...
    gyymz1993閱讀 4,170評(píng)論 2 104
  • 目送君千里 短板 逆鱗
    薔薇颯閱讀 271評(píng)論 0 0
  • 有異性一開始他會(huì)跟你親密,后來就發(fā)展到性,最后來才發(fā)現(xiàn)原來是為了權(quán)利。那么我不禁開始懷疑了,這世界上有真正的愛...
    彭思彤閱讀 242評(píng)論 0 0

友情鏈接更多精彩內(nèi)容