gif展示性能研究

背景

之前蝸牛在瀑布流展示數(shù)據(jù)的時(shí)候發(fā)現(xiàn)gif多哩至,而且每個(gè)gif很大的時(shí)候內(nèi)存會(huì)暴漲酣倾,索性研究了一下圖片的加載過程苗桂,對(duì)比了SDWebImage和FLAnimatedImage 對(duì)gif的處理過程矗烛,做一個(gè)小結(jié)

知識(shí)背景

關(guān)于緩存:

當(dāng)我們通過imageNamed:去讀取一張本地的圖片的時(shí)候玫镐,系統(tǒng)只是在Bundle那查找文件名倒戏,然后把這個(gè)文件放到UIImage里面返回,并沒有做實(shí)際的文件讀取和解碼恐似,當(dāng)UIImage第一次顯示到屏幕上時(shí)候杜跷,內(nèi)部的解碼方法才被調(diào)用,同時(shí)解碼的結(jié)果會(huì)被保存待一個(gè)全局的緩存中去矫夷。在圖片解碼后葛闷,App第一次退到后臺(tái)收到內(nèi)存警告,該圖片的緩存才會(huì)被清空双藕。

當(dāng)我們用imageWithData去讀取一張圖片的時(shí)候淑趾,UIImage底層是通過調(diào)用ImageIO的CGImageSourceCreateWithData方法去創(chuàng)建source對(duì)象的,這里的截圖截自FLAnimatedImage,可以傳一個(gè)shouldCache字段忧陪,默認(rèn)條件下扣泊,這個(gè)參數(shù)是YES近范。所以用imageWithData時(shí)候也不能避免解碼后圖片的緩存,而解碼發(fā)生的時(shí)機(jī)也是在圖片第一次顯示到屏幕上的時(shí)候延蟹。但是用這種方式解碼數(shù)據(jù)是被緩存到CGImage內(nèi)部评矩,如果這個(gè)圖片被釋放,內(nèi)部的解碼數(shù)據(jù)也會(huì)被釋放阱飘。

01

如何避免緩存

手動(dòng)調(diào)用 CGImageSourceCreateWithData() 來創(chuàng)建圖片斥杜,并把 ShouldCacheShouldCacheImmediately 關(guān)掉。這么做會(huì)導(dǎo)致每次圖片顯示到屏幕時(shí)沥匈,解碼方法都會(huì)被調(diào)用蔗喂,造成很大的 CPU 占用。

如何提前解碼

  1. 把圖片用 CGContextDrawImage() 繪制到畫布上咐熙,然后把畫布的數(shù)據(jù)取出來當(dāng)作圖片弱恒。這也是常見的網(wǎng)絡(luò)圖片庫的做法。解碼消耗cpu資源
  2. 直接讀取

1.CGImageSourceCreateWithData(data) 創(chuàng)建 ImageSource棋恼。

2.CGImageSourceCreateImageAtIndex(source) 創(chuàng)建一個(gè)未解碼的 CGImage返弹。

3.CGImageGetDataProvider(image) 獲取這個(gè)圖片的數(shù)據(jù)源。

4.CGDataProviderCopyData(provider) 從數(shù)據(jù)源獲取直接解碼的數(shù)據(jù)爪飘。
ImageIO 解碼發(fā)生在最后一步义起,這樣獲得的數(shù)據(jù)是沒有經(jīng)過顏色類型轉(zhuǎn)換的原生數(shù)據(jù)(比如灰度圖像)。

SDWebImage對(duì)Gif的支持

無論圖片是從disk尋找圖片或者下載完成后sdwebImage都會(huì)調(diào)用sd_animatedWithData:如果是gif圖片走gif的加載邏輯师崎。

02

gif加載邏輯默终,這里注意兩點(diǎn):

1.當(dāng)圖像被解碼后,解碼數(shù)據(jù)是會(huì)被緩存的犁罩,而且被緩存在CGImage中齐蔽,生命周期和image一致。

2.到此得到的image還是沒有發(fā)生解碼床估。

03

那么對(duì)于SDWebImage 中對(duì)于gif圖的解碼發(fā)生在什么時(shí)候呢含滴,

decodedImageWithImage:在SDWebImage中一共有三處調(diào)用

1. 加載完成的時(shí)候,只對(duì)非gif圖片有效丐巫,并且由shouldDecompressImages屬性控制是否解碼谈况,默認(rèn)為YES。

2. 從Disk讀取image的時(shí)候递胧,非gif都有效碑韵,且由shouldDecompressImages屬性控制。默認(rèn)為YES缎脾。

3. 在下載中的時(shí)候祝闻。因?yàn)樾枰鰣D片的漸變出現(xiàn)的效果,sdwebImage會(huì)在didReceiveData中對(duì)加了一部分的圖片做解碼并傳遞給業(yè)務(wù)方遗菠。對(duì)gif和非gif都有效治筒,且由shouldDecompressImages屬性控制屉栓。默認(rèn)為YES。

所以在默認(rèn)條件下耸袜,如果下載的資源為gif時(shí)候友多,在下載的過程中會(huì)對(duì)gif資源進(jìn)行decode,在從disk讀取gif后堤框,也會(huì)做一次decode域滥,由于讀取gif都是用CGImageSourceCreateWithData(默認(rèn)參數(shù)) 或者 imageWithData,所以decode后會(huì)帶有緩存蜈抓。且緩存的生命周期和image綁定启绰。所以解釋了,在有大gif的條件下沟使,為何默認(rèn)條件下進(jìn)入feed流會(huì)引起內(nèi)存飆高委可,但是CPU的的比較低。

decodedImageWithImage:內(nèi)部做了判斷腊嗡,只解碼非gif圖片
默認(rèn)條件下着倾,所以默認(rèn)條件下,gif的解碼放到gif顯示的時(shí)候燕少,解碼后的buffer會(huì)被系統(tǒng)cache卡者,又由于SDWebImage帶有cache緩存,會(huì)cache剛剛使用過的image客们,所以退出feed流頁面后崇决,還是內(nèi)存還是居高不下,只有在手動(dòng)清理webImage的cache后底挫,內(nèi)存才會(huì)下降恒傻。

SDWebImage對(duì)gif的處理

為什么SDWebImage對(duì)gif的處理效率低,而且對(duì)大的gif來說尤為明顯建邓。
我們知道一張圖片從網(wǎng)絡(luò)到顯示盈厘,解碼的過程必不可少,解碼的過程必定需要消耗cpu資源涝缝,解碼后必定會(huì)使得內(nèi)存增大。如果提前解碼緩存譬重,cpu壓力小拒逮,但是內(nèi)存會(huì)高,如果不緩存臀规,cpu壓力大滩援,內(nèi)存使用少。這里的緩存包括代碼的內(nèi)存指定的內(nèi)存緩存和ios系統(tǒng)對(duì)解碼后的圖片的緩存塔嬉。

SDWebImage對(duì)gif的操作玩徊,即使關(guān)掉解碼和存儲(chǔ)到內(nèi)存(sd里的memoryCache)租悄,解碼數(shù)據(jù)還是會(huì)在展示的時(shí)候被系統(tǒng)緩存,所以性能不好恩袱。

FLAnimatedImageView對(duì)gif的處理

FLAnimatedImage 是由Flipboard開源的iOS平臺(tái)上播放GIF動(dòng)畫的一個(gè)優(yōu)秀解決方案泣棋,在內(nèi)存占用和播放體驗(yàn)都有不錯(cuò)的表現(xiàn)。

FLAnimatedImageView的源碼結(jié)構(gòu)非常簡單畔塔,FLAnimatedImage負(fù)責(zé)處理GIF潭辈,然后從緩存中提供給FLAnimatedImageView當(dāng)前需要顯示的圖像

04

關(guān)鍵方法解析

初始化

  1. 初始化緩存字典
  2. 初始化imageSource,根據(jù) kCGImageSourceShouldCache 的官方文檔描述, 所以設(shè)置 kCGImageSourceShouldCache為NO,可以避免系統(tǒng)對(duì)圖片進(jìn)行緩存,

Whether the image should be cached in a decoded form. The value of this key must be a CFBoolean value. The default value is kCFBooleanFalse in 32-bit, kCFBooleanTrue in 64-bit.

  1. 判斷是否gif
  2. 取出gif播放次數(shù)
05
  1. 遍歷每幀圖片
  2. 取出幀圖片
  3. 取出的第一張圖片為GIF動(dòng)畫的封面圖片
  4. 取出幀圖片的信息
  5. 取出幀圖片的展示時(shí)間
06
  1. GIF動(dòng)畫緩存策略
  2. 確認(rèn)最佳的GIF動(dòng)畫的解碼后幀圖片緩存數(shù)量
07

讀取UIImage對(duì)象

  1. 對(duì)索引位置進(jìn)行判斷澈吨,避免出現(xiàn)越界情況
  2. 記錄當(dāng)前取出的幀圖片的索引位置
  3. 判斷GIF動(dòng)畫的幀圖片的是否全部緩存下來了,因?yàn)橛锌赡芫彺娌呗允蔷彺嫠械膸瑘D片
  4. 根據(jù)緩存策略得到接下來需要緩存的幀圖片索引把敢,
  5. 除去已經(jīng)緩存下來的幀圖片索引
  6. 將需要緩存索引扔給其他線程進(jìn)行解碼裝載,解碼的過程是在其他線程,不會(huì)發(fā)生堵塞谅辣,解碼也是通過CGContextDrawImage的方式進(jìn)行解碼修赞。這個(gè)和SDWebImage一致。
  7. 取出幀圖片
  8. 根據(jù)緩存策略清緩存


    08

gif播放部分

gif播放部分在startAnimating時(shí)候開開啟CADisplayLink,進(jìn)行重繪桑阶。

這里有兩點(diǎn)需要注意柏副,

  1. 每次CADisplayLink回調(diào)取的都是cache里的圖片,如果cache里面沒有圖片联逻,就跳過這次繪制機(jī)會(huì)

  2. 累加器的存在意義在與搓扯,避免反復(fù)去繪制同一幀

09

所以FLAnimatedImageViewFLAnimatedImage是標(biāo)準(zhǔn)的生產(chǎn)者和消費(fèi)者的模式。FLAnimatedImage開啟一個(gè)子線程解碼圖片包归,FLAnimatedImageView消費(fèi)圖片進(jìn)行展示锨推。

總結(jié)

其實(shí)SDWebImageFLAnimatedImageView在gif的支持上,性能差別的主要原因有兩個(gè):

  1. FLAnimatedImageView存在一個(gè)緩存策略公壤,每次也只解碼一部分的幀數(shù)據(jù)换可,而且嚴(yán)格把控緩存數(shù)量。而SDWebImage依賴于系統(tǒng)在展示的時(shí)候的統(tǒng)一的全部幀解碼厦幅,緩存的生命周期不好進(jìn)行細(xì)粒度的控制沾鳄。
  2. FLAnimatedImageView解碼的過程放到了子線程中,而SDWebImage默認(rèn)對(duì)gif不解碼确憨,所以解碼發(fā)生在gif第一次顯示時(shí)候译荞,發(fā)生在主線程。

但是對(duì)于比較小且數(shù)量少的gif休弃,其實(shí)兩個(gè)性能差別不大吞歼,但是對(duì)于數(shù)量多或者gif本身比較大時(shí)候,性能差距會(huì)異常明顯塔猾。

注意:最新的SDWebImage可以pod導(dǎo)入SDWebImage/GIF,對(duì)gif的處理自動(dòng)支持了FLAnimatedImage

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末篙骡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糯俗,老刑警劉巖尿褪,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異得湘,居然都是意外死亡杖玲,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門忽刽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來天揖,“玉大人,你說我怎么就攤上這事跪帝〗癫玻” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵伞剑,是天一觀的道長斑唬。 經(jīng)常有香客問我,道長黎泣,這世上最難降的妖魔是什么恕刘? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮抒倚,結(jié)果婚禮上褐着,老公的妹妹穿的比我還像新娘。我一直安慰自己托呕,他們只是感情好含蓉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著项郊,像睡著了一般馅扣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上着降,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天差油,我揣著相機(jī)與錄音,去河邊找鬼任洞。 笑死蓄喇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的交掏。 我是一名探鬼主播妆偏,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼耀销!你這毒婦竟也來了楼眷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤熊尉,失蹤者是張志新(化名)和其女友劉穎罐柳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狰住,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡张吉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了催植。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肮蛹。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖创南,靈堂內(nèi)的尸體忽然破棺而出伦忠,到底是詐尸還是另有隱情,我是刑警寧澤稿辙,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布昆码,位于F島的核電站,受9級(jí)特大地震影響邻储,放射性物質(zhì)發(fā)生泄漏赋咽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一吨娜、第九天 我趴在偏房一處隱蔽的房頂上張望脓匿。 院中可真熱鬧,春花似錦宦赠、人聲如沸陪毡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缤骨。三九已至,卻和暖如春尺借,著一層夾襖步出監(jiān)牢的瞬間绊起,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工燎斩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虱歪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓栅表,卻偏偏與公主長得像笋鄙,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子怪瓶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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