自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

抖音 Android 端圖片優(yōu)化實(shí)踐

移動(dòng)開(kāi)發(fā) Android
本文從抖音Android端圖片優(yōu)化歷程著筆,主要介紹字節(jié)自研BDFresco圖片框架及其在抖音的最佳實(shí)踐、經(jīng)驗(yàn)沉淀、業(yè)務(wù)價(jià)值。通過(guò)分享業(yè)務(wù)視角遇到的一些問(wèn)題和我們的解決思路,希望能拋磚引玉,為遇到類似困擾的伙伴們提供有價(jià)值的參考。

背景介紹

抖音為什么要持續(xù)優(yōu)化圖片能力

圖片能力作為抖音最基礎(chǔ)的能力之一,服務(wù)于抖音各個(gè)業(yè)務(wù)。隨著抖音圖文、電商、IM等多圖業(yè)務(wù)體量的增長(zhǎng),圖片加載量級(jí)越來(lái)越大,對(duì)應(yīng)的圖片帶寬成本也在日益增加。為了降低圖片成本、提升用戶瀏覽圖片體驗(yàn),需要持續(xù)不斷的探索和優(yōu)化圖片能力,在保證圖片展示質(zhì)量的前提下,提升圖片加載速度,降低圖片整體成本,實(shí)現(xiàn)圖片的 "好快省"

BDFresco簡(jiǎn)介

BDFresco是火山引擎veImageX團(tuán)隊(duì)基于開(kāi)源Fresco拓展優(yōu)化的Android端通用基礎(chǔ)的網(wǎng)絡(luò)圖片框架,主要提供圖片網(wǎng)絡(luò)加載、圖像解碼、圖片基礎(chǔ)處理與變換、圖片服務(wù)質(zhì)量監(jiān)控上報(bào)、自研HEIF軟解、內(nèi)存緩存策略、云控配置下發(fā)等能力,目前已覆蓋到字節(jié)幾乎所有App。

圖片

下面將從抖音視角出發(fā),介紹抖音基于BDFresco在圖片方向做了哪些優(yōu)化。

優(yōu)化思路

一張網(wǎng)絡(luò)圖片完整的加載流程如下:

客戶端通過(guò)網(wǎng)絡(luò)獲取業(yè)務(wù)數(shù)據(jù),響應(yīng)內(nèi)容包括對(duì)應(yīng)的圖片數(shù)據(jù),通過(guò)將圖片Url數(shù)據(jù)交給BDFresco加載,正式開(kāi)始圖片的加載流程。BDFresco會(huì)判斷當(dāng)前圖片是否在內(nèi)存緩存及磁盤(pán)緩存,若存在則執(zhí)行對(duì)應(yīng)解碼或渲染操作,若不存在則直接走veImageX-CDN下載,將圖片資源下載到本地后再進(jìn)行解碼和渲染操作。

圖片

圖片加載過(guò)程不僅占用了客戶端內(nèi)存、存儲(chǔ)和CPU等資源,也消耗了網(wǎng)絡(luò)流量和服務(wù)端資源。

圖片的加載流程本質(zhì)上是一個(gè)多級(jí)緩存邏輯,可以將圖片加載流程拆分成4大核心階段,內(nèi)存緩存、圖片解碼、磁盤(pán)緩存、網(wǎng)絡(luò)加載,結(jié)合指標(biāo)監(jiān)控體系,分別針對(duì)各階段進(jìn)行優(yōu)化:

  • 內(nèi)存緩存優(yōu)化:當(dāng)前Android內(nèi)存緩存命中率高達(dá)50%,內(nèi)存緩存以占用App寶貴的內(nèi)存為代價(jià),使得我們可以快速地訪問(wèn)圖片;但內(nèi)存緩存的存在并不會(huì)直接導(dǎo)致App的OOM或者卡頓情況變嚴(yán)重,相反,根據(jù)特定場(chǎng)景配置合理的內(nèi)存緩存配置能夠減少圖片頻繁的解碼和內(nèi)存申請(qǐng),甚至可以帶來(lái)OOM和ANR的優(yōu)化。
  • 圖片解碼優(yōu)化:當(dāng)內(nèi)存緩存失敗后,圖片文件會(huì)進(jìn)行解碼,最終以bitmap形式在內(nèi)存中存在,目前解碼后的bitmap平均大小為800KB,90分位為5MB,99分位更是高達(dá)夸張的11MB,解碼流程需要頻繁申請(qǐng)內(nèi)存,同時(shí)有超過(guò)15%的圖片存在一倍尺寸的浪費(fèi),對(duì)客戶端的性能影響非常大,因此如何減少解碼階段的內(nèi)存申請(qǐng)是我們需要重點(diǎn)解決的問(wèn)題。
  • 磁盤(pán)緩存優(yōu)化:盡管對(duì)比內(nèi)存緩存命中率,磁盤(pán)緩存命中率只有10%,但理論上內(nèi)存中的bitmap在磁盤(pán)中都有對(duì)應(yīng)的原始文件存在,因此想要整體緩存命中率,我們更關(guān)注磁盤(pán)緩存的優(yōu)化,需要通過(guò)合理的磁盤(pán)配置,讓存儲(chǔ)空間利用率更高。
  • 網(wǎng)絡(luò)加載優(yōu)化:雖然網(wǎng)絡(luò)階段失敗率高達(dá)2.5%,但經(jīng)過(guò)數(shù)據(jù)排查和修復(fù),實(shí)際失敗率< 0.1%,優(yōu)化空間不多,考慮到網(wǎng)絡(luò)加載是整體流程耗時(shí)最長(zhǎng)的,耗時(shí)占了近90%,其中主要影響為文件過(guò)大導(dǎo)致的加載耗時(shí)長(zhǎng),因此需要重點(diǎn)解決下發(fā)大文件問(wèn)題,優(yōu)化網(wǎng)絡(luò)加載耗時(shí)。

優(yōu)化過(guò)程

指標(biāo)建設(shè)

在進(jìn)行圖片優(yōu)化之前,需要對(duì)圖片整體質(zhì)量完成一次數(shù)據(jù)盤(pán)點(diǎn),指標(biāo)建設(shè)是至關(guān)重要的一步。通過(guò)建立指標(biāo)系統(tǒng),能夠幫助我們了解圖片現(xiàn)狀、確定優(yōu)化方向和評(píng)估優(yōu)化后的效果。

BDFresco提供日志上報(bào)能力,上報(bào)的圖片日志經(jīng)過(guò)veImageX云端數(shù)據(jù)清洗,最終可以在veImageX云端控制臺(tái)查看圖片質(zhì)量相關(guān)指標(biāo)。從觸發(fā)圖片加載,到內(nèi)存、解碼、磁盤(pán)、網(wǎng)絡(luò)各個(gè)階段都建立了完備的數(shù)據(jù)監(jiān)控體系,覆蓋各階段加載耗時(shí)、成功率、客戶端和CDN緩存命中率、文件大小、內(nèi)存占用、大圖異常監(jiān)控等幾百項(xiàng)指標(biāo)。

圖片

具體舉措

1 內(nèi)存緩存優(yōu)化

1.1 內(nèi)存查找優(yōu)化
內(nèi)存緩存原理

BDFresco是通過(guò)Producer/Consumer接口來(lái)實(shí)現(xiàn)圖片加載的流程,例如網(wǎng)絡(luò)數(shù)據(jù)獲取、緩存數(shù)據(jù)獲取、圖片解碼等多種工作,不同階段由不同Producer實(shí)現(xiàn)類處理,所有的Producer都是一層嵌套一層,產(chǎn)生的結(jié)果由Consumer進(jìn)行消費(fèi)。一個(gè)簡(jiǎn)化后的圖片內(nèi)存緩存邏輯如下:

其中,讀取內(nèi)存或磁盤(pán)緩存是通過(guò)緩存key來(lái)進(jìn)行匹配,緩存key是通過(guò)Uri做轉(zhuǎn)換的,可以簡(jiǎn)單理解成cacheKey==uri,抖音在之前上線過(guò)一個(gè)緩存key優(yōu)化的實(shí)驗(yàn):對(duì)于同個(gè)資源的不同域名,會(huì)剔除host和query參數(shù),即cacheKey被簡(jiǎn)化為scheme://path/name

圖片

優(yōu)化方案

業(yè)務(wù)在進(jìn)行圖片加載時(shí),BDFresco支持傳入U(xiǎn)ri數(shù)組,Uri均是同一資源,指向的是不同veImageX-CDN地址,實(shí)際上內(nèi)部會(huì)將該批Uri(A-B-C)識(shí)別成同一個(gè)緩存key。

如下圖所示,ABC3個(gè)Uri并不完全是按照【A全流程查找->B全流程查找->C全流程查找】的順序執(zhí)行,而是會(huì)先對(duì)ABC各進(jìn)行一次內(nèi)存緩存查找,再按順序進(jìn)行ABC的全流程查找。

圖片

由于ABC為同一資源,只是域名不同,在端上生成的緩存key一致,實(shí)際上的ABC各自的內(nèi)存緩存查找為無(wú)效操作,由于該環(huán)節(jié)在UI線程執(zhí)行,且抖音存在多圖場(chǎng)景,一次滑動(dòng)會(huì)觸發(fā)多次圖片加載邏輯,因此部分場(chǎng)景會(huì)導(dǎo)致卡頓丟幀等情況發(fā)生。

通過(guò)將多余的內(nèi)存查找流程去除,對(duì)大盤(pán)幀率有明顯提升。

1.2 動(dòng)靜圖緩存拆分

抖音圖片的內(nèi)存緩存大小,是根據(jù) java 堆內(nèi)存大小來(lái)進(jìn)行配置,默認(rèn)大小為1/8,即32M或者64M。由于Android 8后,圖片內(nèi)存數(shù)據(jù)不再存儲(chǔ)在java堆上,而是存在native堆,如果繼續(xù)使用堆內(nèi)存大小來(lái)進(jìn)行圖片內(nèi)存緩存大小的配置是不合理的,因此通過(guò)將內(nèi)存緩存大小*2,希望能減少解碼操作,優(yōu)化OOM和ANR指標(biāo)。

實(shí)驗(yàn)后的穩(wěn)定性指標(biāo)顯示,OOM雖然減少了,但是問(wèn)題轉(zhuǎn)換成了native崩潰和ANR都顯著劣化,實(shí)驗(yàn)并不符合預(yù)期。

圖片的緩存命中率和緩存大小成正相關(guān),緩存大小越大,命中率越高,但隨著緩存大小的增大,命中率提升空間會(huì)越來(lái)越小。

結(jié)合實(shí)驗(yàn)結(jié)果來(lái)看,單純?cè)龃缶彺娲笮?huì)導(dǎo)致內(nèi)存水位上升,引發(fā)ANR和native崩潰問(wèn)題,方案并不可行。

圖片

目前動(dòng)圖和靜圖的內(nèi)存緩存使用同一塊緩存塊,BDFresco的緩存管理是LRU的淘汰策略,如果播放動(dòng)圖幀數(shù)過(guò)多,很容易把靜圖緩存給替換掉,重新切換回來(lái)靜圖就需要重新解碼,重新解碼勢(shì)必帶來(lái)性能的損耗和用戶體驗(yàn)的降低,抖音上存在較多此類場(chǎng)景,如IM、個(gè)人頁(yè)動(dòng)靜圖混搭場(chǎng)景。

同時(shí),考慮到直接增大內(nèi)存緩存大小,命中率提升的空間不高,所以嘗試將動(dòng)圖和靜圖緩存做隔離,動(dòng)靜圖各使用一塊內(nèi)存緩存,能夠有效地提升命中率,減少解碼操作。

圖片

最終實(shí)驗(yàn)收益:

  • 抖音通過(guò)拆分動(dòng)靜圖緩存,單塊緩存大小不變,整體緩存增大,日活顯著提升,OOM顯著降低,大盤(pán)幀率顯著正向。
  • 抖極通過(guò)拆分動(dòng)靜圖緩存,單塊緩存大小變?yōu)?/2,整體緩存不變,日活顯著提升,人均使用時(shí)長(zhǎng)顯著正向,OOM顯著降低,大盤(pán)幀率顯著正向。

2 圖片解碼優(yōu)化

2.1 解碼格式優(yōu)化

的內(nèi)存大小圖片長(zhǎng)度圖片寬度

單位像素點(diǎn)占用的字節(jié)數(shù)

單位像素占用的字節(jié)數(shù)由顏色模式Bitmap.Config決定,即ARGB 顏色通道,主要有6種類型:

  • ALPHA_8:只有一個(gè)alpha通道,8bit,每個(gè)像素占1Byte;
  • ARGB_4444:包含紅綠藍(lán)alpha4個(gè)通道,每個(gè)通道4bit,每個(gè)像素占2Byte;
  • ARGB_8888:包含紅綠藍(lán)alpha4個(gè)通道,每個(gè)通道8bit,每個(gè)像素占4Byte;
  • RGB_565:包含紅綠藍(lán)3個(gè)通道,其中紅色占5bit,綠色占6bit,藍(lán)色占5bit,每個(gè)像素占2Byte;
  • RGBA_F16:包含紅綠藍(lán)alpha4個(gè)通道,每個(gè)通道8bit,每個(gè)像素占4Byte;
  • HARDWARE:ARGB_8888的特殊配置,Bitmap會(huì)直接存儲(chǔ)在顯存中。

目前抖音主要使用ARGB_8888和RGB_565兩種配置,ARGB_8888支持透明通道,且顏色質(zhì)量更高,RGB_565不支持透明通道,但整體內(nèi)存占用少了一半,抖音的優(yōu)化思路如下:

  • 低端機(jī)默認(rèn)使用RGB_565進(jìn)行解碼,減少內(nèi)存占用。
  • 抖音部分圖片不攜帶透明通道,如所有的heic圖,但業(yè)務(wù)指定為ARGB_8888,導(dǎo)致透明通道做無(wú)效占用,在內(nèi)存上造成浪費(fèi),因此可以在解碼階段將不攜帶透明通道的圖片強(qiáng)制降級(jí)為RGB_565,在犧牲一定程度的顏色質(zhì)量下減少近一半的內(nèi)存占用和解碼性能損耗。
  • 由于部分bitmap的操作如圓角、高斯模糊等依賴透明通道的渲染,若強(qiáng)制將無(wú)透明通道圖片降級(jí)成565,可能會(huì)導(dǎo)致部分業(yè)務(wù)無(wú)法正常展示,因此需要針對(duì)這類業(yè)務(wù)進(jìn)行加白處理。
2.2 heif解碼內(nèi)存優(yōu)化

優(yōu)化原理:

BDFresco中heic圖解碼原邏輯是通過(guò)jni調(diào)用解碼器的解碼接口,返回解碼后像素?cái)?shù)據(jù),返回到j(luò)ava層再轉(zhuǎn)換成Bitmap對(duì)象展示。原邏輯中存在使用超大臨時(shí)對(duì)象問(wèn)題,會(huì)導(dǎo)致java內(nèi)存開(kāi)銷(xiāo)以及GC,優(yōu)化后減少大對(duì)象創(chuàng)建,直接在native層完成Bitmap對(duì)象構(gòu)建,預(yù)期減少heif圖片解碼耗時(shí),提升一定流暢度。

將原有heif圖片解碼流程從:

圖片

優(yōu)化為流程:

圖片

修復(fù)前:每個(gè)heic圖片解碼時(shí)使用兩個(gè)大數(shù)組:

  • 圖片原始數(shù)據(jù),大小為圖片文件大小,一般在40K-700K之間
  • 圖片解碼后數(shù)據(jù):大小為圖片寬高4,一般在1-11M之間

修復(fù)后: 無(wú)java層大數(shù)組使用,只使用一個(gè)40K-700K的native層的DirectByteBuffer數(shù)組。減少兩個(gè)java層大數(shù)組創(chuàng)建,減少GC發(fā)生概率以及因?yàn)榇髷?shù)組創(chuàng)建導(dǎo)致的OOM問(wèn)題,從而帶來(lái)流暢度以及ANR收益。

圖片

在抖音上開(kāi)實(shí)驗(yàn),性能相關(guān)指標(biāo)均有顯著提升:java內(nèi)存占用減少,heic解碼耗時(shí)減少,Android ANR減少,從而顯著提升圖文的消費(fèi)市場(chǎng),帶動(dòng)了整體使用時(shí)長(zhǎng)收益。

2.3 自適應(yīng)控件解碼

在前面,我們提到有超過(guò)15%的圖片存在一倍尺寸的浪費(fèi),導(dǎo)致解碼階段需要申請(qǐng)大量的內(nèi)存,最終展示在控件上并不需要這么大的bitmap,我們通過(guò)將圖片尺寸resize至控件大小后進(jìn)行解碼,最終解碼出小分辨率的Bitmap,能夠?qū)⒔獯a內(nèi)存申請(qǐng)極致化。

但考慮到圖片浪費(fèi)主要是服務(wù)端下發(fā)過(guò)大的圖片,單純?cè)诮獯a階段限制大小,無(wú)法解決網(wǎng)絡(luò)階段的大圖片問(wèn)題,帶寬浪費(fèi)和網(wǎng)絡(luò)加載耗時(shí)長(zhǎng)問(wèn)題仍然沒(méi)有解決,因此我們將該階段做了前置遷移,在網(wǎng)絡(luò)加載階段進(jìn)行優(yōu)化,具體方案可看4.2節(jié)按需縮放方案。

3 磁盤(pán)緩存優(yōu)化

通過(guò)優(yōu)化客戶端的磁盤(pán)緩存配置來(lái)提升緩存命中率,減少圖片請(qǐng)求量級(jí),在提升圖片加載速度的情況下,也能降低圖片帶寬成本。

磁盤(pán)緩存分為3種:主磁盤(pán)、small磁盤(pán)、獨(dú)立磁盤(pán);各磁盤(pán)空間存在上限,采用LRU替換算法,目前抖音主要使用主磁盤(pán)和獨(dú)立磁盤(pán),整體流程如下:圖片默認(rèn)存儲(chǔ)在主磁盤(pán),圖片被替換概率較高;若業(yè)務(wù)指定獨(dú)立磁盤(pán)cacheName,則指定圖片會(huì)單獨(dú)使用一個(gè)磁盤(pán),被替換概率低。

圖片

  • 主磁盤(pán)存儲(chǔ)空間增大:抖音Android端存儲(chǔ)空間上限為40M,考慮到該值為fresco的默認(rèn)值,配置值主要參考當(dāng)年設(shè)備的存儲(chǔ)空間,因此可以針對(duì)存儲(chǔ)空間較多的設(shè)備,增加圖片存儲(chǔ)配置,提升磁盤(pán)緩存命中率。
  • 實(shí)驗(yàn)結(jié)果表明:隨著存儲(chǔ)空間的增大,磁盤(pán)緩存命中率顯著上漲,進(jìn)一步帶來(lái)圖片量級(jí)的減少,當(dāng)圖片存儲(chǔ)上限提升至80M時(shí),Android大盤(pán)量級(jí)-5%
  • 獨(dú)立磁盤(pán)推廣:針對(duì)復(fù)用率高的圖片場(chǎng)景,推薦接入獨(dú)立磁盤(pán)緩存,可以減少被其他業(yè)務(wù)圖片LRU替換的幾率,提升圖片的磁盤(pán)緩存命中率。
  • 以IM表情包為例,我們拉取IM業(yè)務(wù)的圖片緩存命中率數(shù)據(jù)分析,表情包命中率僅有7%,對(duì)比同樣使用獨(dú)立磁盤(pán)的IM普通圖片的28%和個(gè)人頁(yè)主態(tài)的31%,表情包磁盤(pán)命中率偏低。
  • 將IM表情包接入獨(dú)立磁盤(pán)后,表情包請(qǐng)求量減少27%

4 網(wǎng)絡(luò)加載優(yōu)化

4.1 圖片格式優(yōu)化
常見(jiàn)圖片格式
  • image:原圖,未經(jīng)過(guò)veImageX壓縮處理。
  • JPEG:全稱為Joint Photographic Experts Group(聯(lián)合圖像專家組),于1992發(fā)布,是一種有損壓縮的光柵圖像文件格式,壓縮率越高圖片質(zhì)量越差,同時(shí)不支持透明通道。
  • PNG:全稱為Portable Network Graphics(便攜式網(wǎng)絡(luò)圖形),在1997年3月作為知識(shí)性RFC 2083發(fā)布,于2004年作為ISO/IEC標(biāo)準(zhǔn)發(fā)布,PNG也是一種柵格圖形格式,但支持無(wú)損壓縮,同時(shí)也支持?jǐn)y帶透明通道信息。
  • WebP:是一種由谷歌開(kāi)發(fā)的圖片格式,于2010年發(fā)布,支持有損壓縮和無(wú)損壓縮圖片文件格式,提供更高的壓縮率和更快的加載速度。對(duì)比jpeg和png格式,在相同圖片質(zhì)量的情況下,文件體積能減少30%+,同時(shí)WebP 圖片格式還支持透明通道和動(dòng)畫(huà),目前抖音Android所有版本均支持Webp格式。
  • HEIC(BVC1):基于火山引擎自研BVC算法進(jìn)行封裝的圖片(17項(xiàng)第一,火山自研編碼器在MSU大賽多項(xiàng)奪冠[https://www.toutiao.com/article/6951287905268843011/?upstream_biz=doubao&source=m_redirect]),通常的文件后綴名為heic,對(duì)比Webp格式,在相同圖片質(zhì)量的情況下,文件體積能再減少30%+,帶寬收益更加明顯。但heic格式也存在缺點(diǎn):由于高效編碼會(huì)導(dǎo)致解碼性能損耗略有增加,但體積較小也會(huì)帶來(lái)網(wǎng)絡(luò)耗時(shí)的降低,最終總的加載耗時(shí)基本打平或略有降低,目前抖音Android端已全量使用自研BVC軟解實(shí)現(xiàn)解碼。
  • vvic:字節(jié)基于 BVC2算法自研的圖片格式,采用的是VVC的圖片編碼格式,又稱BVC2編碼格式,對(duì)比heic的BVC1壓縮率更高。
heic格式推廣

當(dāng)前veImageX平臺(tái)支持最好的是heic編碼格式,但到22年初,抖音Android端覆蓋率不足50%,直接通過(guò)提升業(yè)務(wù)的heic占比能夠大幅減少帶寬成本,提升圖片加載速度。

  • JPEG->heic,大幅減少帶寬成本80%以上,加載速度提升30%+
  • webp->heif,個(gè)人頁(yè)動(dòng)圖平均文件大小-25.33%,加載速度提升30%+

在做heif動(dòng)圖實(shí)驗(yàn)推廣時(shí),發(fā)現(xiàn)個(gè)人頁(yè)UI幀率存在大幅劣化,在高低端設(shè)備均有6-8幀的幀率下降,實(shí)驗(yàn)無(wú)法上線,針對(duì)該問(wèn)題,我們對(duì)heif動(dòng)圖的解碼緩存邏輯進(jìn)行一次優(yōu)化,提出了heif動(dòng)圖獨(dú)立緩存優(yōu)化方案。

heif動(dòng)圖獨(dú)立緩存

動(dòng)圖原理

在圖片文件下載完成解析成字節(jié)流,動(dòng)圖正式播放之前,BDFresco會(huì)進(jìn)行預(yù)解碼,當(dāng)動(dòng)圖正式播放時(shí),會(huì)根據(jù)動(dòng)圖調(diào)度器的播放順序?qū)itmap渲染到屏幕上,并且在播放過(guò)程中會(huì)主動(dòng)預(yù)解碼下一幀,如當(dāng)前需要播放第5幀,會(huì)同步解碼第6幀率。其中預(yù)解碼操作均在子線程中進(jìn)行。

圖片

不同調(diào)度器的核心區(qū)別為:當(dāng)子線程預(yù)解碼速度過(guò)慢,下一幀需要播放的Bitmap不存在時(shí),是繼續(xù)返回當(dāng)前幀重復(fù)播放,等待子線程進(jìn)行解碼,還是返回下一幀,直接在主線程進(jìn)行解碼渲染。

  • SmoothSlidingFrameScheduler:默認(rèn)調(diào)度器,在子線程預(yù)解碼速度跟不上播放速度時(shí),會(huì)降低動(dòng)圖的播放速度,如重復(fù)播放當(dāng)前幀,保證不在主線程進(jìn)行解碼,會(huì)導(dǎo)致動(dòng)圖播放不流程,但對(duì)頁(yè)面性能非常好,不會(huì)引起卡頓。
  • DropFramesFrameScheduler:嚴(yán)格按照?qǐng)D片的時(shí)間標(biāo)準(zhǔn)進(jìn)行播放,若預(yù)解解碼速度太慢,則直接在主線程進(jìn)行解碼,以保證對(duì)應(yīng)幀能夠在對(duì)應(yīng)時(shí)間內(nèi)進(jìn)行解碼并且渲染到屏幕上,缺點(diǎn)是會(huì)在主線程進(jìn)行解碼,可能會(huì)引起頁(yè)面的卡頓。
  • 自定義調(diào)度器:業(yè)務(wù)自定義實(shí)現(xiàn)getFrameNumberToRender接口,支持倒序播放、跳幀播放等特殊邏輯。

獨(dú)立緩存

heif動(dòng)圖掉幀問(wèn)題經(jīng)過(guò)排查,發(fā)現(xiàn)heif動(dòng)圖采用了一個(gè)新的播放調(diào)度邏輯FixedSlidingHeifFrameScheduler:動(dòng)圖無(wú)任何預(yù)解碼邏輯,在需要播放對(duì)應(yīng)幀時(shí),直接在主線程進(jìn)行解碼,即播放一幀解碼一幀,這也導(dǎo)致了Heif動(dòng)圖在播放過(guò)程中需要在主線程占用大量CPU資源進(jìn)行解碼。

圖片

為什么heif動(dòng)圖必須在主線程解碼呢?

對(duì)比其他動(dòng)圖支持任意幀解碼,heif動(dòng)圖采用了幀間壓縮的方式,引入了I幀P幀的概念,I幀為關(guān)鍵幀,包含了當(dāng)前圖像的完整信息,能夠獨(dú)立解碼;P幀為差別幀,沒(méi)有完整的畫(huà)面數(shù)據(jù),只有與前一幀的畫(huà)面差別的數(shù)據(jù),無(wú)法獨(dú)立進(jìn)行解碼,解碼需要依賴前一幀數(shù)據(jù)。

由于AndroidBDFresco的內(nèi)存緩存為L(zhǎng)RU替換,Bitmap隨時(shí)有可能被回收,因此針對(duì)Heif動(dòng)圖的解碼,必須嚴(yán)格按照動(dòng)圖順序進(jìn)行解碼,否則會(huì)導(dǎo)致Heif動(dòng)圖播放過(guò)程中出現(xiàn)花屏綠屏等問(wèn)題。

方案思考:

  • 從源頭解決,優(yōu)化heif動(dòng)圖的編碼解碼邏輯,但目前Heif的幀結(jié)構(gòu)就決定了解碼器的解碼邏輯,如果需要支持指定幀解碼,就得改造Heif編碼格式,方案不可行。
  • 不在主線程進(jìn)行解碼,專門(mén)開(kāi)一個(gè)子線程做heif動(dòng)圖的解碼,主線程需要渲染某一幀的時(shí)候,就切到子線程去解碼,解碼完成通知主線程做渲染,但方案對(duì)BDFresco的解碼流程改造較大,且不支持內(nèi)存緩存,方案待定。
  • 抖音Android&iOS雙端共用一個(gè)解碼器,但iOS實(shí)驗(yàn)并無(wú)幀率劣化,原因在于iOS的圖片內(nèi)存緩存是可控的,不會(huì)有不符合預(yù)期的緩存釋放,因此Android端可以嘗試借鑒該思路
  • 給heif動(dòng)圖單獨(dú)開(kāi)辟一個(gè)新的內(nèi)存緩存塊,且對(duì)解碼后的Bitmap進(jìn)行強(qiáng)引用,即不會(huì)被動(dòng)釋放內(nèi)容,也不會(huì)被其他圖片LRU替換。方案優(yōu)點(diǎn)在于能夠完美復(fù)用老的解碼邏輯,也支持子線程預(yù)解碼,只需要將Bitmap單獨(dú)緩存即可實(shí)現(xiàn)。
  • 由于Bitmap是強(qiáng)引用,緩存塊也無(wú)上限,方案存在內(nèi)存無(wú)限增長(zhǎng)的可能,因此需要有一個(gè)主動(dòng)釋放時(shí)機(jī),即能減少內(nèi)存占用,也能保證解碼順序不被影響。因此我們嘗試關(guān)聯(lián)view的detach方法,當(dāng)動(dòng)圖控件在快速滑動(dòng)時(shí),會(huì)主動(dòng)釋放不可見(jiàn)View上對(duì)應(yīng)的Bitmap。

圖片

經(jīng)過(guò)實(shí)驗(yàn),最終采取了獨(dú)立緩存方案,在取得帶寬收益的同時(shí),個(gè)人頁(yè)幀率無(wú)明顯劣化。

4.2 按需縮放
背景

圖片加載流程最終會(huì)將解碼后的bitmap渲染在控件上,當(dāng)bitmap大小大于控件時(shí),實(shí)際對(duì)用戶感官并無(wú)影響,圖片最終展示的像素值不會(huì)超過(guò)控件占據(jù)的空間,當(dāng)圖片大小 >> 控件大小時(shí):

  • 造成一定程度的帶寬浪費(fèi);
  • 圖片過(guò)大,客戶端性能損耗嚴(yán)重;
  • 不同業(yè)務(wù)對(duì)同一張圖片進(jìn)行圖片裁剪,沒(méi)有考慮圖片尺寸碎片化問(wèn)題,導(dǎo)致veImageX-CDN緩存命中率顯著下降,最終造成回源成本的暴漲。

圖片

解決方案

在圖片展示時(shí)上報(bào)對(duì)應(yīng)的bitmap和控件大小,從上報(bào)的數(shù)據(jù)來(lái)看,存在大量業(yè)務(wù)請(qǐng)求的圖片大小遠(yuǎn)大于控件。因此,需要采用一種通用的方案,在滿足圖片質(zhì)量的前提下,客戶端提供一套控件規(guī)范,根據(jù)控件大小將圖片收斂至固定大小,保證圖片尺寸和展示控件基本一致,同時(shí)減少圖片碎片化問(wèn)題。

個(gè)人頁(yè)、同城、推薦等多個(gè)業(yè)務(wù)均存在雙列封面場(chǎng)景,這里以雙列封面為例子:

圖片

收益
  • 視覺(jué)搜索場(chǎng)景文件大小 -83.39%,內(nèi)存大小 -66.57%
  • veImageX-CDN緩存命中率提升 + 6.99%,回源請(qǐng)求數(shù)減少 -23.79%

5 異?;謴?fù)

盡管前面我們對(duì)圖片的加載流程做了一系列優(yōu)化,但因?yàn)槎兑舯旧韴D片量級(jí)大,部分業(yè)務(wù)如電商、IM等對(duì)圖片清晰度有較高的要求,且存在圖片放大和長(zhǎng)圖展示等操作,業(yè)務(wù)會(huì)進(jìn)行超大圖加載,直接將圖片直接加載進(jìn)內(nèi)存,單張圖片內(nèi)存甚至高達(dá)100M+,無(wú)論在磁盤(pán)IO階段,還是內(nèi)存解碼或者Bitmap拷貝過(guò)程中均會(huì)申請(qǐng)大量?jī)?nèi)存,最終導(dǎo)致卡頓、ANR甚至OOM崩潰,因此需要一套兜底方案來(lái)解決圖片OOM頻發(fā)問(wèn)題,提升圖片加載的可靠性。

抖音在系統(tǒng)內(nèi)存觸頂時(shí),會(huì)通過(guò)釋放圖片內(nèi)存來(lái)緩解壓力:監(jiān)聽(tīng)系統(tǒng)內(nèi)存的告警回調(diào),根據(jù)不同級(jí)別釋放不同大小的圖片內(nèi)存緩存,降低發(fā)生OOM和ANR的幾率,但因大圖存在,仍然存在大量OOM。

OOM兜底

內(nèi)存是一個(gè)全局指標(biāo),并不能直接通過(guò)OOM堆棧確定異常原因,因?yàn)镺OM發(fā)生的時(shí)候內(nèi)存可能處于高水位狀態(tài),有可能申請(qǐng)了一個(gè)小對(duì)象就直接觸發(fā)異常。但關(guān)注到崩潰中Top5的堆棧大部分和圖片堆棧有關(guān)系,可以合理懷疑是App內(nèi)圖片頻繁申請(qǐng)大內(nèi)存導(dǎo)致。

因此針對(duì)高頻的圖片解碼和內(nèi)存拷貝邏輯,增加兜底邏輯,當(dāng)代碼發(fā)生OOM,主動(dòng)catch,并通過(guò)清除圖片占用的內(nèi)存緩存來(lái)釋放部分內(nèi)存,降低內(nèi)存水位:

  • 清除兩級(jí)內(nèi)存緩存,解碼內(nèi)存緩存+未解碼內(nèi)存緩存
  • 清除接入層緩存的動(dòng)圖預(yù)覽幀

實(shí)驗(yàn)結(jié)果表明,盡管部分OOM轉(zhuǎn)換成native崩潰,但整體影響用戶大幅下降,實(shí)驗(yàn)符合預(yù)期。

總結(jié)

總體來(lái)看,抖音在建設(shè)了圖片的全鏈路監(jiān)控后,根據(jù)數(shù)據(jù)分析對(duì)圖片加載流程做了不少優(yōu)化。

  1. 提升了圖片加載速度和性能
  2. 減少了圖片的總成本

從收益角度來(lái)看,大致可以分為成本優(yōu)化和客戶端體驗(yàn)優(yōu)化兩方面。成本收益主要是圖片帶寬成本的降低,體驗(yàn)收益體現(xiàn)在日活和OOM指標(biāo)上,并且隨著各種優(yōu)化方案推廣到更多的業(yè)務(wù)線,收益也在持續(xù)增加。

本文簡(jiǎn)要介紹了抖音基于BDFresco的圖片優(yōu)化最佳實(shí)踐、經(jīng)驗(yàn)沉淀、業(yè)務(wù)收益。由于篇幅所限,本文對(duì)探索歷程、具體實(shí)現(xiàn)等細(xì)節(jié)內(nèi)容有所省略,但仍希望能給業(yè)內(nèi)同仁們一點(diǎn)啟發(fā)或者參考借鑒。目前BDFresco已集成到火山引擎veImageX產(chǎn)品,對(duì)行業(yè)開(kāi)放使用中,如需體驗(yàn)抖音同款圖片優(yōu)化能力,可以到火山引擎veImageX官網(wǎng)申請(qǐng)使用。

參考:火山引擎veImageX提供端到端一站式的整體圖片解決方案,包含圖片及素材托管、圖像處理與壓縮、分發(fā)、客戶端編解碼及圖片加載SDK全鏈路能力,官網(wǎng)地址:https://www.volcengine.com/product/imagex

責(zé)任編輯:龐桂玉 來(lái)源: 字節(jié)跳動(dòng)技術(shù)團(tuán)隊(duì)
相關(guān)推薦

2022-06-06 12:19:08

抖音功耗優(yōu)化Android 應(yīng)用

2022-03-29 13:27:22

Android優(yōu)化APP

2022-07-06 13:02:00

高延時(shí)電商直播主播互動(dòng)

2022-06-01 09:18:37

抖音ReDex算法優(yōu)化

2023-11-03 17:02:18

抖音直播畫(huà)質(zhì)優(yōu)化

2022-07-19 16:47:53

Android抖音

2023-03-03 15:43:23

抖音世界杯畫(huà)質(zhì)優(yōu)化

2024-11-13 08:47:24

2022-04-28 15:07:41

抖音內(nèi)存泄漏Android

2022-07-20 22:55:39

直播OOM抖動(dòng)

2024-03-12 17:13:51

2024-10-31 08:22:56

2022-06-23 11:19:14

抖音春節(jié)發(fā)券

2023-03-28 08:28:34

2021-06-28 05:19:32

抖音電腦

2025-02-20 08:00:00

2022-08-26 16:24:19

抖音體系化建設(shè)項(xiàng)目

2022-05-07 15:51:47

Android資源文件文件名

2019-03-07 15:04:37

抖音快手同城

2013-03-27 09:17:17

Android開(kāi)發(fā)AndroidList
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)