京喜APP - 圖片庫(kù)優(yōu)化

作者 | 何樂(lè)樂(lè)
來(lái)源 | 掘金,點(diǎn)擊閱讀原文查看作者更多文章
介紹
京喜APP早期開(kāi)發(fā)主要是快速原生化迭代替代原有H5,提高用戶(hù)體驗(yàn),在這期間也積累了不少性能問(wèn)題。之后我們開(kāi)始進(jìn)行一些性能優(yōu)化相關(guān)的工作,本文主要是介紹京喜圖片庫(kù)相關(guān)優(yōu)化策略以及關(guān)于圖片相關(guān)的一些關(guān)聯(lián)知識(shí)。
圖片性能問(wèn)題
作為電商APP,圖片在各個(gè)業(yè)務(wù)場(chǎng)景被大量使用。我們需要做到盡可能降低網(wǎng)絡(luò)消耗/內(nèi)存消耗/硬盤(pán)消耗,同時(shí)不降低圖片質(zhì)量,提高圖片加載速度,給用戶(hù)帶來(lái)更好的使用體驗(yàn)。基于這些性能目標(biāo),我們也通過(guò)初步性能評(píng)估梳理出了一些性能問(wèn)題:
圖片加載慢/流量消耗高
圖片鏈接主要由后臺(tái)接口下發(fā),下發(fā)圖片格式和尺寸由每個(gè)業(yè)務(wù)后臺(tái)指定。部分業(yè)務(wù)沒(méi)有使用更小的圖片格式比如WebP,或圖片尺寸過(guò)大,都會(huì)使圖片過(guò)大導(dǎo)致網(wǎng)絡(luò)消耗高。特別是網(wǎng)絡(luò)狀況不佳的場(chǎng)景,圖片加載過(guò)慢給用戶(hù)帶來(lái)不好的體驗(yàn)。同時(shí)也會(huì)導(dǎo)致更多的I/O讀寫(xiě)和解碼耗時(shí),造成更多的電量消耗。
圖片內(nèi)存占用高
經(jīng)過(guò)初步的APP內(nèi)存使用評(píng)估,圖片內(nèi)存消耗占APP總內(nèi)存消耗的比例最高,特別是大尺寸圖片會(huì)占用很多內(nèi)存。一方面APP占用太高內(nèi)存退到后臺(tái)容易被系統(tǒng)殺死,導(dǎo)致下次打開(kāi)重新啟動(dòng)影響體驗(yàn)。另一方面APP大量使用內(nèi)存,容易被系統(tǒng)殺死產(chǎn)生OOM。特別是我們目前有大量的低端設(shè)備用戶(hù),設(shè)備內(nèi)存相對(duì)比較低。
優(yōu)化方向
基于上面分析出的一些性能問(wèn)題,我們對(duì)圖片框架進(jìn)行了整體重構(gòu)優(yōu)化。一方面是降低圖片網(wǎng)絡(luò)傳輸,提高圖片加載速度。另一方面是減少圖片內(nèi)存消耗。

最小化網(wǎng)絡(luò)傳輸
京東圖片服務(wù)器提供了多種處理功能,例如圖片格式轉(zhuǎn)換,圖片降質(zhì),圖片縮放,圖片圓角等功能。這些功能通過(guò)在圖片URL中添加特定參數(shù)實(shí)現(xiàn),圖片服務(wù)器會(huì)根據(jù)參數(shù)設(shè)置提前將圖片處理完成并保存到CDN服務(wù)器。我們可以通過(guò)添加圖片處理參數(shù),減少圖片傳輸大小。
雖然后臺(tái)可以提前進(jìn)行URL預(yù)處理,下發(fā)已添加過(guò)圖片參數(shù)的圖片URL。但是由于對(duì)接后臺(tái)業(yè)務(wù)很多,每個(gè)業(yè)務(wù)圖片參數(shù)設(shè)置差異很大無(wú)法統(tǒng)一,而且可能會(huì)造成性能影響,例如沒(méi)有使用webP圖片格式,下發(fā)太大的圖片尺寸。同時(shí)考慮到推動(dòng)各業(yè)務(wù)后臺(tái)修改成本也很高,并且前端機(jī)型多,不同機(jī)型需要使用不同的圖片尺寸。另外也不方便灰度降級(jí)功能,后續(xù)功能修改也不方便。所以在客戶(hù)端進(jìn)行圖片URL預(yù)處理是更好的方式,可以統(tǒng)一控制,也方便之后功能更新。
圖片URL預(yù)處理

圖片庫(kù)在網(wǎng)絡(luò)圖片加載前,檢測(cè)是否是京東域名的圖片URL。如果域名匹配,圖片框架先對(duì)圖片URL進(jìn)行預(yù)處理,預(yù)處理包括域名統(tǒng)一,添加縮放參數(shù),添加webP參數(shù),添加降質(zhì)參數(shù)的方式減少圖片網(wǎng)絡(luò)傳輸大小。
提示:因?yàn)楹笈_(tái)返回的圖片URL可能會(huì)帶有一部分圖片處理參數(shù),例如 https://img11.360buyimg.com/img/pingou-head/25.jpg!webp ,直接追加圖片參數(shù)可能會(huì)導(dǎo)致圖片處理參數(shù)不生效,或格式錯(cuò)誤導(dǎo)致加載失敗。所以轉(zhuǎn)換時(shí)會(huì)先將所有圖片參數(shù)提前計(jì)算出來(lái),之后一起處理,避免添加重復(fù)參數(shù)。
域名統(tǒng)一
目前圖片服務(wù)器提供了多個(gè)圖片域名可使用,例如m.360buyimg.com,img10.360buyimg.com等多個(gè)域名。m.360buyimg.com主要提供給移動(dòng)端使用。但是由于對(duì)接了各種業(yè)務(wù)后臺(tái),導(dǎo)致接口會(huì)下發(fā)不同的域名圖片。圖片使用不同域名可能會(huì)導(dǎo)致以下問(wèn)題:
不利于緩存復(fù)用 - 圖片框架通常默認(rèn)以URL字符串生成圖片緩存key,不同域名導(dǎo)致生成不同的緩存key。硬盤(pán)緩存無(wú)法復(fù)用會(huì)導(dǎo)致圖片重復(fù)下載,內(nèi)存緩存無(wú)法復(fù)用導(dǎo)致同樣的圖片占用多份內(nèi)存。
不利于HTTP/2連接復(fù)用 - 大部分界面圖片比較多,很多場(chǎng)景都會(huì)同時(shí)加載多張圖片,特別是首屏通常會(huì)加載幾十張圖片。當(dāng)加載多個(gè)圖片時(shí),每個(gè)域名都需要重新建立HTTPS連接,經(jīng)歷DNS解析/TCP連接/TLS握手過(guò)程(目前一次HTTPS請(qǐng)求創(chuàng)建過(guò)程大概耗時(shí)50-150ms)。如果利用HTTP/2鏈接復(fù)用就只需要?jiǎng)?chuàng)建一次HTTPS請(qǐng)求,之后的圖片請(qǐng)求可以減少這部分的耗時(shí)。
所以在預(yù)處理時(shí),如果是京東域名的圖片,將圖片URL域名統(tǒng)一替換為m.360buyimg.com。
追加圖片參數(shù)
圖片縮放
很多業(yè)務(wù)后臺(tái)返回的原始圖片URL的size都比客戶(hù)端實(shí)際顯示的size要大。一方面導(dǎo)致使用更多的網(wǎng)絡(luò)流量造成浪費(fèi)。另一方面會(huì)導(dǎo)致占用更多內(nèi)存。同時(shí)因?yàn)閳D片size和實(shí)際顯示size不一致導(dǎo)致像素不對(duì)齊,GPU需要做額外的插值處理,也會(huì)一定的影響渲染性能。所以我們通過(guò)添加縮放參數(shù)的方式,指定圖片服務(wù)器下發(fā)更小和更匹配實(shí)際顯示size的圖片尺寸。
動(dòng)態(tài)scale計(jì)算尺寸
因?yàn)閕OS設(shè)備主要使用2x/3x的分辨率,所以業(yè)務(wù)方使用API時(shí)需要傳入對(duì)應(yīng)的ptsize大小,圖片庫(kù)內(nèi)部根據(jù)設(shè)備的scale進(jìn)行動(dòng)態(tài)計(jì)算出真實(shí)的像素寬高。
提示:android設(shè)備因?yàn)槠聊徊町惐容^大,更適合使用固定的scale。太多的圖片尺寸不利于CDN緩存,無(wú)緩存的時(shí)候需要對(duì)圖片進(jìn)行相關(guān)參數(shù)處理,圖片處理本身是耗時(shí)操作。
Scale降級(jí)
低端機(jī)降級(jí) - 對(duì)于部分3xscale的低端設(shè)備,因?yàn)闄C(jī)器本身內(nèi)存比較低,使用3x分辨率計(jì)算出來(lái)的圖片像素寬高比較大,會(huì)造成更多的內(nèi)存消耗以及解碼/渲染更多的性能消耗。所以對(duì)于寬高超過(guò)一定要求的圖片,降級(jí)到使用2x分辨率來(lái)計(jì)算像素寬高,減少設(shè)備性能消耗。
iPad降級(jí) - 因?yàn)槟壳癆PP并沒(méi)有針對(duì)iPad做特定優(yōu)化,所以iPad設(shè)備下默認(rèn)是放大顯示。這會(huì)導(dǎo)致在iPad下圖片尺寸計(jì)算出來(lái)特別大。所以也是針對(duì)iPad圖片尺寸做了特定限制,防止下發(fā)圖片尺寸過(guò)大。
大圖片降級(jí) - 正常情況下圖片寬/高不應(yīng)該超過(guò)屏幕寬/高。為了防止部分業(yè)務(wù)使用過(guò)大的圖片size,所以添加了一個(gè)限制,最終生成的圖片像素尺寸不能超過(guò)屏幕寬/高。
降質(zhì)
圖片服務(wù)器支持0-100的圖片質(zhì)量參數(shù)設(shè)置,通過(guò)降低圖片質(zhì)量可以減少圖片大小,但是質(zhì)量降低太多也會(huì)影響圖片的觀看體驗(yàn)。我們將圖片質(zhì)量參數(shù)設(shè)置為q70,指定圖片服務(wù)器下發(fā)70%質(zhì)量的圖片。對(duì)于大部分業(yè)務(wù),一方面可以大幅減少圖片下載大小,同時(shí)也可以保證觀看體驗(yàn)。通過(guò)添加圖片降質(zhì)參數(shù)至少可以減少30-40%的圖片大小。
使用WebP
按照Google官方的數(shù)據(jù),與PNG相比,WebP無(wú)損圖像的字節(jié)數(shù)要少26%。WebP有損圖像比同類(lèi)JPG圖像字節(jié)數(shù)少25-34%。圖片服務(wù)器支持轉(zhuǎn)換webP格式,可以減少圖片大小。針對(duì)png/jpg圖片格式,添加webP參數(shù),指定圖片服務(wù)器下發(fā)webp格式。雖然webP相比png/jpg圖片解碼需要更長(zhǎng)時(shí)間,但相對(duì)網(wǎng)絡(luò)傳輸速度提升還是很大。
提示:由于目前圖片服務(wù)器并不支持GIF轉(zhuǎn)webP,GIF并沒(méi)有做處理。
URL預(yù)處理緩存
添加輕量緩存,提高URL轉(zhuǎn)換性能。因?yàn)閁RL轉(zhuǎn)換本身有一定的耗時(shí),而且單個(gè)圖片URL可能會(huì)多次加載/多次轉(zhuǎn)換。轉(zhuǎn)換后的URL會(huì)直接保存到緩存中,下次使用可以直接返回。緩存key由URL+相關(guān)圖片轉(zhuǎn)換參數(shù)拼接組成。
圖片API設(shè)計(jì)
圖片處理參數(shù)通過(guò)options設(shè)置,默認(rèn)使用q70圖片質(zhì)量以及webP格式。業(yè)務(wù)方在調(diào)用加載圖片方法時(shí)傳入,下面是iOS端的API:
imageView6.jx.setImage(url: URL(string: "https://img11.360buyimg.com/img/pingou-head/25.jpg"),
placeholder: nil, options: [.imageSize(CGSize(width: 40, height: 40))])
降低圖片內(nèi)存消耗
png/jpg等圖片格式在顯示之前都需要經(jīng)過(guò)解碼生成一張位圖,之后根據(jù)位圖創(chuàng)建紋理傳給GPU做渲染。一張位圖的內(nèi)存消耗大概是像素寬x像素高x位深。通常圖片使用的是RGBA,位深為32位。一張500px_500px的大概1MB內(nèi)存。對(duì)于GIF圖片因?yàn)楸旧碛卸鄮宰罱K的內(nèi)存消耗為單幀內(nèi)存x幀數(shù)。
我們的優(yōu)化方向一方面是通過(guò)圖片縮放的方式,減少圖片位圖的內(nèi)存消耗。另一方面限制圖片緩存上限避免緩存使用過(guò)高。
圖片縮放
通過(guò)上面URL預(yù)處理過(guò)程讓圖片服務(wù)器下發(fā)更小的圖片格式,已經(jīng)降低了一部分內(nèi)存。但是URL預(yù)處理只處理了jd域名的jpg/png圖片,對(duì)于GIF或京東域名外的圖片沒(méi)有處理,包括一部分URL轉(zhuǎn)換后加載失敗的圖片。所以對(duì)于這部分圖片,我們會(huì)在端側(cè)做圖片縮放的處理,降低內(nèi)存消耗。例如一張300px_300px包含100幀的GIF圖片,實(shí)際顯示區(qū)域只有50px_50px,優(yōu)化后總內(nèi)存消耗可從30MB+內(nèi)存降低到3MB。
設(shè)置圖片緩存上限
圖片緩存的設(shè)計(jì)目的是減少圖片解碼消耗。圖片第一次使用的時(shí)候,將圖片進(jìn)行解碼后的位圖保存在內(nèi)存中,這樣可以避免下次使用時(shí)避免重復(fù)解碼。雖然圖片內(nèi)存高可以盡量避免圖片重復(fù)解碼,但是占用太高內(nèi)存也會(huì)導(dǎo)致APP后臺(tái)被系統(tǒng)殺掉或產(chǎn)生OOM等問(wèn)題。所以我們應(yīng)該將內(nèi)存緩存控制在一定范圍內(nèi)。
例如iOS的第三方圖片庫(kù)SDWebImage/Kingfisher默認(rèn)都使用系統(tǒng)庫(kù)NSCache來(lái)實(shí)現(xiàn)內(nèi)存緩存。雖然NSCache會(huì)在設(shè)備內(nèi)存緊張時(shí)回收內(nèi)存,但是默認(rèn)并不限制可保存內(nèi)存最大字節(jié)數(shù),所以在設(shè)備內(nèi)存可用的情況下內(nèi)存可以一直增加。所以通過(guò)設(shè)置圖片緩存上限,防止圖片緩存占用太高內(nèi)存。圖片緩存定義了一個(gè)默認(rèn)的初始值上限,之后對(duì)于3x大屏幕設(shè)備和高端設(shè)備(內(nèi)存比較高),適當(dāng)增加更多內(nèi)存上限。
優(yōu)化成果

其他收益
域名統(tǒng)一 - 減少了10%+的重復(fù)圖片下載和內(nèi)存消耗。同時(shí)減少之前多域名圖片加載時(shí)重復(fù)創(chuàng)建HTTPS請(qǐng)求的過(guò)程,減少圖片加載時(shí)間。
其他策略
加載異常處理
因?yàn)樯倭繄D片通過(guò)URL預(yù)處理轉(zhuǎn)換后,可能會(huì)存在圖片不存在的異常場(chǎng)景導(dǎo)致加載失敗。所以當(dāng)發(fā)生圖片加載失敗時(shí),我們還是需要加載原始圖片URL。但是這里需要屏蔽一些特殊的加載錯(cuò)誤,避免非必要的加載,例如無(wú)網(wǎng)絡(luò)/網(wǎng)絡(luò)超時(shí)/主動(dòng)取消加載等錯(cuò)誤。之后會(huì)將錯(cuò)誤圖片URL上報(bào)到后臺(tái),方便之后調(diào)整URL轉(zhuǎn)換策略,也可以發(fā)現(xiàn)一部分錯(cuò)誤的圖片URL推動(dòng)業(yè)務(wù)修改。同時(shí)將這部分連接加入到錯(cuò)誤連接緩存中,避免下次重復(fù)執(zhí)行預(yù)處理和重復(fù)上報(bào)。
線(xiàn)上配置
目前存在的一些功能,例如URL預(yù)處理/統(tǒng)一域名/WebP使用等功能,都添加了線(xiàn)上配置,方便灰度/降級(jí)。一在出現(xiàn)問(wèn)題時(shí)可以降級(jí)某些功能,新功能上線(xiàn)時(shí)也可以進(jìn)行灰度測(cè)試。
大圖檢測(cè)
需要有一個(gè)機(jī)制及時(shí)發(fā)現(xiàn)圖片不符合規(guī)范的問(wèn)題。一方面我們通過(guò)線(xiàn)上灰度檢測(cè)的方式,當(dāng)發(fā)現(xiàn)大圖片時(shí)會(huì)進(jìn)行上報(bào),后續(xù)推動(dòng)業(yè)務(wù)方進(jìn)行優(yōu)化。另一方面我們?cè)谌粘y(cè)試階段,會(huì)開(kāi)啟Debug檢測(cè)工具,當(dāng)檢測(cè)到大圖片時(shí),通過(guò)圖片翻轉(zhuǎn)/高亮背景顏色的方式提醒業(yè)務(wù)開(kāi)發(fā)同學(xué)進(jìn)行優(yōu)化。
Flutter圖片庫(kù)優(yōu)化
目前京喜APP有10+個(gè)二級(jí)界面是基于Flutter開(kāi)發(fā),所以我們也針對(duì)Flutter圖片加載做了一些優(yōu)化。
對(duì)接原生圖片庫(kù)
因?yàn)镕lutter框架自帶圖片庫(kù)只提供內(nèi)存圖片緩存,并不支持硬盤(pán)緩存,所以會(huì)導(dǎo)致圖片重復(fù)下載。所以我們通過(guò)重寫(xiě)ImageProvider,當(dāng)加載網(wǎng)絡(luò)圖片時(shí),通過(guò)Channel調(diào)用原生圖片庫(kù),原生圖片庫(kù)下載圖片到本地磁盤(pán)后,返回圖片文件目錄。之后Flutter通過(guò)文件目錄加載解碼圖片顯示。這樣一方面可以利用原生圖片庫(kù)相關(guān)優(yōu)化能力,同時(shí)也可以復(fù)用圖片硬盤(pán)緩存避免重復(fù)下載。
減少內(nèi)存消耗
使用Image組件時(shí),通過(guò)設(shè)置cacheHeight/cacheWidth,將圖片解碼為置頂像素寬高的位圖尺寸,減少內(nèi)存消耗。同時(shí)因?yàn)镕lutter內(nèi)存消耗相對(duì)原生更高,所以在Flutter界面關(guān)閉時(shí),通過(guò)調(diào)用imageCache方法清除圖片內(nèi)存消耗降低內(nèi)存消耗。
GIF優(yōu)化
動(dòng)畫(huà)優(yōu)化 - 因?yàn)橥ǔJ褂肍lutter都是混合棧的機(jī)制,原生和Flutter界面在頁(yè)面導(dǎo)航中相互跳轉(zhuǎn)。所以當(dāng)Flutter界面存在GIF圖片時(shí),跳轉(zhuǎn)到原生以后GIF動(dòng)畫(huà)還會(huì)一直執(zhí)行。所以我們通過(guò)在Image組件內(nèi)監(jiān)聽(tīng)Flutter engine發(fā)送的生命周期通知,當(dāng)Flutter界面不在棧頂時(shí),停止GIF動(dòng)畫(huà)執(zhí)行,減少內(nèi)存和CPU消耗。
減少解碼次數(shù) - Flutter框架內(nèi)部對(duì)GIF渲染的處理方式,在屏幕每一幀判斷當(dāng)前需要顯示的GIF幀,之后對(duì)該GIF幀進(jìn)行解碼之后渲染。因?yàn)椴⒉粫?huì)把解碼過(guò)的幀保存,所以會(huì)導(dǎo)致頻繁解碼導(dǎo)致內(nèi)存波動(dòng)大。經(jīng)過(guò)優(yōu)化,對(duì)已經(jīng)解碼過(guò)的幀進(jìn)行保存,避免重復(fù)解碼的消耗,同時(shí)避免內(nèi)存的波動(dòng)。
優(yōu)化前內(nèi)存波動(dòng)很明顯

優(yōu)化后內(nèi)存傾于平穩(wěn)

20提示:保存每一幀也會(huì)導(dǎo)致更多的內(nèi)存消耗。目前APP中通常是小尺寸的GIF所以整體可控。可以考慮設(shè)置緩沖區(qū)上限來(lái)控制緩存的圖片幀數(shù)避免內(nèi)存過(guò)高。
后續(xù)優(yōu)化方向
更優(yōu)的緩存算法
優(yōu)先移除最大內(nèi)存 - iOS系統(tǒng)NSCache實(shí)現(xiàn)。通過(guò)設(shè)置最大內(nèi)存數(shù),當(dāng)內(nèi)存不足時(shí)優(yōu)先移除最大的值。
LRU緩存 - 優(yōu)先淘汰最久未使用的圖片內(nèi)存。對(duì)于很多二級(jí)界面的場(chǎng)景,用戶(hù)打開(kāi)界面后并不會(huì)再次打開(kāi)。但是因?yàn)檫@些圖片緩存是最后使用,所以清除內(nèi)存時(shí)也會(huì)最后移除,但是在這種場(chǎng)景下就不太合適。
界面棧管理 - 當(dāng)界面關(guān)閉時(shí)將該界面的所有的圖片內(nèi)存移除,但是對(duì)于經(jīng)常會(huì)打開(kāi)的界面會(huì)導(dǎo)致頻繁圖片編解碼也不太合適。
所以針對(duì)不同的業(yè)務(wù)場(chǎng)景使用不同的回收方式可能更加合適:
對(duì)于購(gòu)物車(chē)/我的訂單這類(lèi)界面,用戶(hù)每次加載的圖片基本固定,所以更適合在內(nèi)存中常駐,當(dāng)內(nèi)存消耗過(guò)高時(shí)再回收。
對(duì)于商詳/搜索商品列表這類(lèi)界面,通常商品列表展示的圖片不一樣并且用戶(hù)也不會(huì)頻繁進(jìn)某一個(gè)特定的商詳,所以更適合優(yōu)先移除這部分的內(nèi)存。
對(duì)于部分彈窗功能,圖片顯示后并不會(huì)再次使用,可以考慮不添加到內(nèi)存中。
使用更好的圖片格式
使用更好的圖片格式通常可以帶來(lái)更小的圖片字節(jié)大小。同時(shí)因?yàn)閴嚎s率的提高,可以在減少大小的同時(shí)提高圖片質(zhì)量。
提示:使用系統(tǒng)支持硬解碼的圖片格式更有優(yōu)勢(shì)。硬解碼就是使用GPU進(jìn)行解碼,相比使用CPU軟解碼性能更好更省電。
APNG/動(dòng)畫(huà)WebP代替GIF - 按照Google官方的說(shuō)法,GIF轉(zhuǎn)換為有損WebP的字節(jié)數(shù)縮小了64%,而無(wú)損WebP字節(jié)數(shù)縮小了19%。所以使用動(dòng)畫(huà)WebP可以減少更多的網(wǎng)絡(luò)流量傳輸。APNG是Mozilla推出的基于PNG的動(dòng)圖格式并且完全支持RGBA,相比GIF可以減少20%+的圖片大小。而且GIF本身只支持256色索引顏色以及1位alpha(加上透明度后,邊緣會(huì)出現(xiàn)明顯的鋸齒),使用APNG/WebP也可以帶來(lái)相比GIF更好的顯示效果。
提示:相比GIF,WebP的解碼比GIF占用更多的CPU資源。有損WebP的解碼時(shí)間是GIF的2.2倍,而無(wú)損WebP的解碼時(shí)間是GIF的1.5倍。
HEIC - HEIC是基于H.265視頻編碼格式推出的圖片格式。HEIC相比WebP可以減少20%+的圖片大小,并且編解碼性能更好。在系統(tǒng)兼容性上,Android 9.0以上的系統(tǒng)支持HEIC。蘋(píng)果在iOS14以上系統(tǒng)才提供了WebP硬解碼,之前的系統(tǒng)只能使用軟解碼,而HEIC在iOS11之后的機(jī)器上都已經(jīng)支持硬解碼,不過(guò)并不支持瀏覽器。
AVIF - AVIF是基于AV1編碼格式推出的圖片格式。AVIF相比WebP可以減少30%+的圖片大小。不過(guò)目前只有Android 12以上的版本支持。
提示:這里主要是以VP8編碼格式的WebP,VP9編碼格式的WebP整體性能和HEIC差異不大。
不過(guò)這些圖片格式需要圖片服務(wù)器支持之后才能使用。
Flutter
雖然我們對(duì)Flutter圖片庫(kù)做了一些優(yōu)化,但總體上還有很多優(yōu)化空間。包括業(yè)界有在使用的基于紋理的圖片方案。在原生側(cè)將圖片解碼后,通過(guò)Flutter引擎創(chuàng)建紋理。之后講圖片紋理id傳遞給Flutter進(jìn)行渲染。這樣可以統(tǒng)一在原生側(cè)管理圖片內(nèi)存緩存,優(yōu)化之前Flutter和原生都分別有一份內(nèi)存緩存的方式。而且針對(duì)于混合棧的導(dǎo)航棧方式,也可以更好的進(jìn)行圖片內(nèi)存回收。另外針對(duì)Flutter,需要提供更靈活的圖片內(nèi)存回收策略,避免內(nèi)存消耗過(guò)高。
提示:紋理可以復(fù)用內(nèi)存中的位圖緩存,所以并不會(huì)導(dǎo)致更多的內(nèi)存占用。紋理方式大概能減少30%的內(nèi)存消耗相比Flutter引擎圖片庫(kù),主要是一些其他對(duì)象使用導(dǎo)致。
優(yōu)化H5圖片加載
我們可以通過(guò)攔截WebView圖片加載的方式,讓原生圖片庫(kù)來(lái)下載圖片之后傳遞圖片二進(jìn)制數(shù)據(jù)給WebView顯示。
減少流量消耗
通過(guò)這種方式,我們可以將原生圖片庫(kù)URL預(yù)處理相關(guān)功能支持到H5圖片,減少H5加載過(guò)程中圖片流量消耗,提高圖片加載速度。同時(shí)因?yàn)锳PP原生和WebView圖片緩存機(jī)制是相互獨(dú)立的,所以通過(guò)統(tǒng)一在原生側(cè)管理圖片緩存,可以減少相同圖片的重復(fù)下載。
支持更多圖片格式
例如在iOS系統(tǒng)上,WKWebView目前只支持PNG/JPG/GIF圖片格式。所以我們可以通過(guò)在原生端實(shí)現(xiàn)下載WebP/HEIC圖片,之后對(duì)圖片進(jìn)行解碼再傳給WebView,這樣就可以支持其他圖片格式的顯示。
提示:因?yàn)閃ebView不支持直接傳遞位圖二進(jìn)制數(shù)據(jù)顯示,所以需要提前轉(zhuǎn)換為PNG/JPG二進(jìn)制數(shù)據(jù)傳遞。所以對(duì)于其他圖片格式增加一次PNG/JPG編碼過(guò)程會(huì)造成更多的性能消耗。不過(guò)對(duì)于Android系統(tǒng)應(yīng)該可以在web內(nèi)核層優(yōu)化減少這塊消耗。
圖片分辨率
對(duì)于同樣的圖片,因?yàn)榉直媛什煌瑢?dǎo)致重復(fù)下載。如果能在硬盤(pán)緩存做優(yōu)化,分辨率小的圖片可以直接硬盤(pán)緩存中的大圖片,這樣就避免圖片重復(fù)下載,在解碼時(shí)生成更小分辨率的位圖也不會(huì)占用更多內(nèi)存。
總結(jié)
本文并沒(méi)有講底層圖片加載庫(kù)的具體實(shí)現(xiàn),目前圖片庫(kù)不管是直接用第三方庫(kù)還是自研圖片庫(kù)實(shí)現(xiàn)方式通常差異不大。我們更多是關(guān)注自身業(yè)務(wù)以及如何利用圖片服務(wù)器能力最大化改善網(wǎng)絡(luò)圖片加載性能。所以部分策略可能不一定針對(duì)所有APP都合適,應(yīng)該針對(duì)自身業(yè)務(wù)場(chǎng)景仔細(xì)評(píng)估優(yōu)化方案。
擴(kuò)展鏈接
WebP https://developers.google.com/speed/webp
手淘圖片庫(kù)HEIC使用 https://zhuanlan.zhihu.com/p/265881086
動(dòng)畫(huà)WebP和GIF比較 https://developers.google.com/speed/webp/faq#why_should_i_use_animated_webp
WebP支持 https://caniuse.com/?search=webp
APNG支持 https://caniuse.com/?search=apng
AVIF https://jakearchibald.com/2020/avif-has-landed/
