為了解決小程序生成分享到朋友圈圖片的問(wèn)題,我們開(kāi)啟了 畫(huà)家計(jì)劃--- 一個(gè)小程序圖片生成庫(kù)。該計(jì)劃已開(kāi)源,可移步: github.com/Kujiale-Mob… 。 大家知道 Canvas 的繪制有很多很蛋疼的坑,其中一個(gè)是它的 drawImage 方法,該方法在 IDE 中可以直接設(shè)置為網(wǎng)絡(luò)圖片的 url 進(jìn)行繪制,但在真機(jī)上無(wú)法這樣做。有這個(gè)坑后,我們就需要先把圖片通過(guò) download 下載到本地后,才能進(jìn)行繪制。所以你的小程序中,如果有頻繁繪制一些圖片的需求,而又需要用到網(wǎng)絡(luò)圖片素材,這就導(dǎo)致每次繪制都要重新下載素材圖片,產(chǎn)生很大的繪制性能問(wèn)題。 小程序本身是未提供文件 LRU 之類的緩存機(jī)制的。為了讓我們的 畫(huà)家計(jì)劃 圖片生成的更快,我們自己開(kāi)發(fā)了的小程序文件進(jìn)行 LRU 存儲(chǔ)的相關(guān)代碼。這樣我們就無(wú)需重復(fù)下載可能會(huì)頻繁使用到的繪圖素材,大大增加了繪圖速度。 介紹下小程序的緩存系統(tǒng)小程序的緩存分為數(shù)據(jù)緩存,和文件緩存兩部分。而文件緩存又分為臨時(shí)文件緩存,和本地文件存儲(chǔ)。其中本地文件存儲(chǔ)的大小限制為 10M。 數(shù)據(jù)緩存我們可以使用小程序提供的一套異步和同步的方法來(lái)增刪查結(jié)構(gòu)化數(shù)據(jù)。同一個(gè)微信用戶,同一個(gè)小程序的存儲(chǔ)上限為 10MB。如果空間不足時(shí)會(huì)在小程序級(jí)別進(jìn)行 LRU,也就是不經(jīng)常使用的小程序的數(shù)據(jù)緩存區(qū)域會(huì)被全部清空。 詳細(xì)見(jiàn)微信官方文檔: developers.weixin.qq.com/miniprogram… 注:數(shù)據(jù)緩存區(qū)在體驗(yàn)版、開(kāi)發(fā)版、和線上版都共用一套,并不會(huì)隔離。文件臨時(shí)緩存我們?cè)谡{(diào)用 wx.downloadFile 或者 wx.chooseImage 等獲取文件或圖片的方式成功后,我們會(huì)得到這個(gè)文件或圖片的臨時(shí)存儲(chǔ)路徑。文檔上寫的是 臨時(shí)路徑的生命周期是在本次小程序啟動(dòng)期間內(nèi) 。 不過(guò)沒(méi)有對(duì)存儲(chǔ)大小的限制進(jìn)行說(shuō)明,所以理論上不管多大文件都可以進(jìn)行臨時(shí)緩存,當(dāng)然如果太大肯定會(huì)造成某些神奇的錯(cuò)誤吧。 本地存儲(chǔ)我們?cè)讷@得臨時(shí)文件后,可以通過(guò)調(diào)用接口 wx.saveFile 把臨時(shí)文件存儲(chǔ)到本地空間中,本地空間存儲(chǔ)限制為 10M。如果存儲(chǔ)滿了后,后面的文件就無(wú)法存儲(chǔ)成功了,會(huì)報(bào)超出最大存儲(chǔ)上限的錯(cuò)誤。 而我們現(xiàn)在需要做的就是在這個(gè)本地存儲(chǔ)空間上,開(kāi)辟一個(gè)空間,作為我們下載文件的存儲(chǔ)空間,因空間有限,所以我們需要對(duì)這塊空間進(jìn)行 LRU 管理。 有關(guān)本地存儲(chǔ)相關(guān)的接口可看以下文檔: developers.weixin.qq.com/miniprogram… 注:把臨時(shí)文件通過(guò)調(diào)用 saveFile 成功后,這個(gè)臨時(shí)文件路徑就無(wú)效了。切記切記。文件 LRU 存儲(chǔ)實(shí)現(xiàn)小程序端的本地存儲(chǔ)有 10M 限制,但卻無(wú) LRU,現(xiàn)在我們需要結(jié)合上面提到的小程序三種存儲(chǔ)方式來(lái)實(shí)現(xiàn)一套小程序文件下載的 LRU 機(jī)制。 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì){ 'key': { 'path': // 文件的存儲(chǔ)路徑 'time': // 時(shí)間戳,用來(lái)記錄文件的最后訪問(wèn)時(shí)間,當(dāng)存儲(chǔ)不夠時(shí),會(huì)選擇最遠(yuǎn)未被訪問(wèn)的文件進(jìn)行刪除 'size': // 文件大小 } .... 'totalSize': // 所有存儲(chǔ)文件的當(dāng)前總大小 } 復(fù)制代碼 其中我們用下載的 url 作為 key。 以上數(shù)據(jù)結(jié)構(gòu)會(huì)存在在數(shù)據(jù)緩存區(qū)(后續(xù)我們會(huì)把這個(gè)區(qū)域稱為 storage 區(qū)),并且在下載器構(gòu)建之時(shí)會(huì)從 storage 中讀取到內(nèi)存中。以后的文件操作,也會(huì)實(shí)時(shí)同步到 storage 中記錄的文件信息。 你可以理解為,storage 中存儲(chǔ)了文件的基本信息,而 path 就相當(dāng)于指向這個(gè)實(shí)際文件的指針。 總體流程設(shè)計(jì)
容錯(cuò)因?yàn)?storage 的存儲(chǔ),和文件操作都是異步的,所以有可能存在兩者不一致的情況。此處的不一致情況分兩種 第一種,storage 的某文件信息被刪除了,但文件本身卻因?yàn)槌霈F(xiàn)神奇錯(cuò)誤而未被刪除。另外文件添加成功了,但 storage 中卻未添加成功也屬于此情況。 第二種,storage 中文件信息刪除失敗,但是文件卻被刪除了。 以上兩種性質(zhì)不同,所以也需要區(qū)別對(duì)待。針對(duì)第一種會(huì)導(dǎo)致文件的存儲(chǔ)空間和 storage 中記錄的文件信息不一致,也即出現(xiàn)了游離的文件(未被 storage 跟蹤)。 而第二種,相當(dāng)于存在了空指針,此種情況是絕對(duì)需要避免的,因?yàn)檫@會(huì)導(dǎo)致你在拿出一個(gè)不存在的文件使用。會(huì)直接導(dǎo)致嚴(yán)重bug。 針對(duì)以上兩種特殊情況,做了以下容錯(cuò)的處理。首先我們要保證文件的刪除操作一定要在 storage 成功之后進(jìn)行。這樣保證了第二種不會(huì)出錯(cuò)。 而針對(duì)第一種游離文件的情況。我們這邊會(huì)在 saveFile 的時(shí)機(jī)進(jìn)行兜底處理。如果存在了游離文件,最終會(huì)導(dǎo)致我們空間總大小計(jì)算不一致,這可能最終會(huì)導(dǎo)致,我們外部邏輯認(rèn)為可以存儲(chǔ),但實(shí)際存儲(chǔ)空間已經(jīng)滿了,這樣就會(huì)導(dǎo)致 saveFile 報(bào)錯(cuò),在 saveFile 出錯(cuò)后,不管啥原因,我們都把涉及到本策略存儲(chǔ)相關(guān)的內(nèi)容全部清空掉,重新來(lái)過(guò)。因?yàn)槲覀円恢庇?tempFilePath 兜底,所以即使這種情況出現(xiàn),也不會(huì)影響用戶正常使用。只是會(huì)影響一點(diǎn)用戶體驗(yàn)(畢竟一下子沒(méi)有以前的緩存了)。 注:之所以不像保證第二種情況的方式來(lái)保證第一種情況,是因?yàn)槲矣X(jué)得不需要為處理極少會(huì)出現(xiàn)的錯(cuò)誤場(chǎng)景而去浪費(fèi)性能,影響用戶體驗(yàn)。只要我們做好兜底,即使這種錯(cuò)誤情況萬(wàn)一真的出現(xiàn),整個(gè)系統(tǒng)也不會(huì)因此出問(wèn)題,還是會(huì)正常使用。 寫在后面小程序有很多的坑。目前市面上很多小程序性能體驗(yàn)并不是很好。所以為了做一款高性能的小程序,是需要我們花大量的時(shí)間去試錯(cuò),琢磨的。踩坑不止,生命不惜。 |