[轉(zhuǎn)]教你寫Android ImageLoader框架之基本架構(gòu)(一)

本文轉(zhuǎn)自Mr.Simple的博客,如侵刪

前言

在Android開發(fā)中,ImageLoader應(yīng)該算得上是最重要的開源庫之一,由于項目原因(不能使用開源庫),前段時間自己也是需要實現(xiàn)一個簡單的ImageLoader囚霸,因此誕生了這個庫,我們暫且叫它為SimpleImageLoader激才。就目前而言拓型,你上網(wǎng)查ImageLoader資料的時候,基本上能夠找到很簡單的實現(xiàn)瘸恼,基本上一個類就把所有的工作給做了劣挫,這就顯得很不專業(yè)了嘛,很多時候我們不只是需要實現(xiàn)功能钞脂,而是希望能夠在實現(xiàn)功能的同時在設(shè)計層面有所提升揣云。

SimpleImageLoader分享出來的主要目的并不是說替代那些著名開源庫,而是提供一個簡單的冰啃、又有一定參考價值的ImageLoader實現(xiàn)讓一些需要幫助的人學(xué)習(xí)邓夕,在深入了解實現(xiàn)的同時學(xué)到知識,也能夠體會到在設(shè)計一個開源庫時應(yīng)該要做哪些考慮阎毅、做哪些取舍焚刚、有什么模式,當(dāng)然在了解了ImageLoader的實現(xiàn)之后再去使用專業(yè)的開源庫也會更加的得心應(yīng)手扇调,出現(xiàn)問題的時候自己也能夠不太費力地去究其原因矿咕。在提升自己的同時也能夠了解一些開源庫的設(shè)計基本原則,這也是我的博客中一直主張的觀點狼钮。

當(dāng)然限于本人水平有限碳柱,有bug在所難免,但是我們這里是以學(xué)習(xí)為目的熬芜,了解ImageLoader的設(shè)計與實現(xiàn)才是我們最重要的目的莲镣,一些細(xì)節(jié)不必在意,可以在你深入的學(xué)習(xí)過程中修改你認(rèn)為不合理或者錯誤的地方涎拉。如果你有好的實現(xiàn)方法或者有好的建議瑞侮,也都請指教一二;如果你認(rèn)為我寫的東西爛得不值一提鼓拧,那你就深深地埋藏在心里吧半火。


基本架構(gòu)

一般來說,ImageLoader的實現(xiàn)都是基于線程池季俩,在第一版的我也是使用線程池來加載圖片钮糖,但是后面的版本卻換成了跟SimpleNet類似的架構(gòu)模式,原因是覺得線程池剛啟動的時候會稍微慢一些酌住,我感覺不太爽就換了線程模型店归。當(dāng)然我也會把線程池的版本在另外的分支上給出,這樣給一些需要的朋友參考赂韵。
SimpleImageLoader的基本結(jié)構(gòu)如圖1所示娱节。

圖1

看到這幅圖看過SimpleNet網(wǎng)絡(luò)框架的朋友應(yīng)該是會熟悉一些,基本結(jié)構(gòu)與SimpleNet很相似祭示,其實主要也是我們比較喜歡把架構(gòu)圖畫成這種分層樣式肄满,感覺比較好理解,

SimpleImageLoader類是用戶的入口质涛,用戶在通過配置類初始化SimpleImageLoader之后就可以向SimpleImageLoader提交加載圖片的請求了稠歉。SimpleImageLoader內(nèi)部維護(hù)了一個請求隊列,用戶提交的加載圖片的請求會在內(nèi)部被封裝成BitmapRequest對象汇陆,然后將這些對象放到請求隊列中怒炸。在創(chuàng)建隊列時會創(chuàng)建用戶指定數(shù)量(默認(rèn)為CPU個數(shù) + 1)的線程來加載圖片,這些線程在內(nèi)部命名為RequestDispatcher毡代,它們在run函數(shù)中不斷地獲取隊列中的加載請求阅羹,然后交給對應(yīng)的Loader加載圖片勺疼。

為了方便用戶的擴(kuò)展,我們引入了Loader這個抽象捏鱼,因為在SimpleImageLoader中只支持兩種圖片uri的加載执庐,即網(wǎng)絡(luò)圖片uri和本地文件的uri。網(wǎng)絡(luò)圖片一般以"http:// "或者"https:// "開頭导梆,而本地圖片的uri格式卻是"file://"開頭轨淌,SimpleImageLoader內(nèi)部通過圖片uri的格式的不同使用不同的Loader來加載圖片,這樣后續(xù)用戶就可以注冊Loader來實現(xiàn)其他格式的加載看尼,例如"drawable:// + 圖片名"來加載res/drawable中的圖片等递鹉。這樣保證了SimpleImageLoader可加載圖片uri格式的可擴(kuò)展性。Loader會通過LoaderManager來進(jìn)行管理藏斩,如果需要注冊自己的Loader實現(xiàn)躏结,則調(diào)用LoaderManager的register函數(shù)即可。如果你傳遞進(jìn)去的圖片uri是無效灾茁,例如格式錯誤窜觉,那么LoaderManager會返回一個默認(rèn)的Loader,這個默認(rèn)的Loader名為NullLoader北专,它其實什么也不做禀挫,只是為了防止在外部進(jìn)行判空而已,這種模式成為Null Object設(shè)計模式拓颓。當(dāng)然语婴,在加載圖片之前我們會從緩存中讀取,如果有緩存我們則不加載驶睦。

Loader加載完圖片之后會先更新UI砰左,即將圖片顯示到對應(yīng)的ImageView上,在構(gòu)造BitmapRequest時內(nèi)部已經(jīng)將圖片的uri設(shè)置為ImageView的tag了场航。圖片加載完成后判斷ImageView的tag和uri是否相等缠导,如果相等則將圖片顯示到ImageView上,否則不更新ImageView溉痢。這一步很重要僻造,很多朋友在使用ImageLoader時出現(xiàn)問題基本上就是由于沒有設(shè)置ImageView的tag。

加載圖片的先后順序是由加載策略決定的孩饼,策略相關(guān)的內(nèi)容沒有在架構(gòu)圖中給出髓削。加載策略決定了請求在隊列中的排序,在將請求添加到隊列中時會給每個請求設(shè)置一個序號镀娶,隊列將根據(jù)這個序號對請求進(jìn)行排序立膛。這樣我們就可以知道哪個請求是先添加進(jìn)來的,也可以很方便的實現(xiàn)自己的策略類來定制自己的加載策略梯码,比如最后加載到隊列中的請求最先加載宝泵。比如我們在ListView滾動時好啰,最后添加到隊列中的圖片請求應(yīng)該是我們最急需顯示的,我們它們就在手機(jī)的當(dāng)前屏幕鲁猩,而前面的請求對應(yīng)的ImageView已經(jīng)被復(fù)用坎怪,即使它們加載完成它們也不會被顯示罢坝,因為ImageView的tag已經(jīng)變化了廓握。因此,策略的靈活性依然很重要嘁酿。

在加載完圖片并且更新UI之后隙券,我們會將圖片緩存起來。內(nèi)置的緩存類型有四種闹司,無緩存娱仔、內(nèi)存緩存、sd卡緩存游桩、內(nèi)存和sd卡的雙緩存牲迫,這四種緩存都實現(xiàn)了Cache接口,如果你這四種緩存類型還不能滿足你的需求借卧,那么你可以實現(xiàn)Cache接口盹憎,然后實現(xiàn)自己的緩存邏輯,然后在配置ImageLoader時設(shè)置需要的緩存類型(具體配置后續(xù)說明)铐刘,如果不配置則默認(rèn)使用的是內(nèi)存緩存陪每。這里我們又看到了一個面向接口編程的例子,即SimpleImageLoader只依賴于Cache接口的抽象镰吵,而不是說依賴于某個具體的緩存類檩禾,這樣用戶就可以很方面的實現(xiàn)自己的緩存邏輯,并且將緩存實現(xiàn)注入到sdk中疤祭。當(dāng)然盼产,上述的Loader、加載策略實現(xiàn)也是基于同樣的理論基礎(chǔ)勺馆,就是說過很多遍的“面向接口編程”戏售。

恩,是時候捋一捋這個執(zhí)行流程了谓传。

用戶調(diào)用displayImage請求加載圖片蜈项,SimpleImageLoader將這個加載圖片請求封裝成一個Request,然后加入到隊列中续挟。幾個色瞇瞇的調(diào)度子線程不斷地從隊列中獲取請求紧卒,然后根據(jù)uri的格式獲取到對應(yīng)的Loader來加載圖片。在加載圖片之前首先會查看緩存中是否含有目標(biāo)圖片(具體細(xì)節(jié)在后續(xù)的博客再細(xì)說)诗祸,如果有緩存則使用緩存跑芳,否則加載目標(biāo)圖片轴总。獲取到圖片之后,我們會將圖片投遞給ImageView進(jìn)行更新博个,如果該ImageView的tag與圖片的uri是一樣的怀樟,那么則更新ImageView,否則不處理盆佣。
使用ImageView的tag與圖片的uri進(jìn)行對比是為了防止圖片錯位顯示的問題往堡,這在ImageLoader中是很重要的一步。
如果目標(biāo)圖片沒有緩存共耍,第一次從uri中加載后會加入緩存中虑灰,當(dāng)然從sdcard中加載的圖片我們只會緩存到內(nèi)存中,而不會再緩存一份到sd卡的另一個目錄中痹兜。這樣穆咐,整個加載過程也就完成了。


SimpleImageLoader工程結(jié)構(gòu)圖


效果圖

上述效果中有四張圖片是顯示不出來的字旭,因為在圖片uri列表中有三張是我手機(jī)中的圖片对湃,模擬器中沒有,因此顯示加載失敗遗淳。還有一張是無效的uri拍柒,也是加載失敗。
GitHub

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洲脂,一起剝皮案震驚了整個濱河市斤儿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恐锦,老刑警劉巖往果,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異一铅,居然都是意外死亡陕贮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門潘飘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肮之,“玉大人,你說我怎么就攤上這事卜录「昵埽” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵艰毒,是天一觀的道長筐高。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么柑土? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任蜀肘,我火速辦了婚禮,結(jié)果婚禮上稽屏,老公的妹妹穿的比我還像新娘扮宠。我一直安慰自己,他們只是感情好狐榔,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布坛增。 她就那樣靜靜地躺著,像睡著了一般荒叼。 火紅的嫁衣襯著肌膚如雪轿偎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天被廓,我揣著相機(jī)與錄音,去河邊找鬼萝玷。 笑死嫁乘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的球碉。 我是一名探鬼主播蜓斧,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼睁冬!你這毒婦竟也來了挎春?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤豆拨,失蹤者是張志新(化名)和其女友劉穎直奋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體施禾,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡脚线,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了弥搞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邮绿。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖攀例,靈堂內(nèi)的尸體忽然破棺而出船逮,到底是詐尸還是另有隱情,我是刑警寧澤粤铭,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布挖胃,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏冠骄。R本人自食惡果不足惜伪煤,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凛辣。 院中可真熱鬧抱既,春花似錦、人聲如沸扁誓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝗敢。三九已至捷泞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寿谴,已是汗流浹背锁右。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留讶泰,地道東北人咏瑟。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像痪署,于是被迫代替她去往敵國和親码泞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

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