學(xué)會(huì)了嗎(jstool京東)京東 let trash do the talk,京東APP中Flutter探索及優(yōu)化,基層選舉工作條例,
目錄:
1.京東scriptable
2.京東 jason
3.auto.js 京東
4.京東nlp
5.京東ui
6.github action 京東
7.京東git
8.打開(kāi)京東 app ,購(gòu)物更輕松
9.京東doga
10.我想問(wèn)一下京東
1.京東scriptable
前言隨著Flutter穩(wěn)定版本逐步迭代更新,京東APP內(nèi)部的Flutter業(yè)務(wù)也日益增多,F(xiàn)lutter開(kāi)發(fā)為我們提供了高效的開(kāi)發(fā)環(huán)境、優(yōu)秀的跨平臺(tái)適配、豐富的功能組件及動(dòng)畫(huà)、接近原生的交互體驗(yàn),但隨之也帶來(lái)了一些OOM問(wèn)題,通過(guò)線上監(jiān)控信息和Observatory工具結(jié)合分析我們發(fā)現(xiàn)問(wèn)題的原因是由于Flutter頁(yè)面中加載的大量圖片導(dǎo)致的內(nèi)存溢白玉髓的寓意是什么出,這也是在原生開(kāi)發(fā)中常見(jiàn)的問(wèn)題之一,F(xiàn)lutter官方為我們提供的Image widget實(shí)現(xiàn)圖片加載及顯示,只有了解Flutter中圖片的加載原理及圖片內(nèi)存管理方式才能真正發(fā)現(xiàn)問(wèn)題的本質(zhì),本文將重點(diǎn)介紹Flutter中圖片的加載原理,使用過(guò)程中有哪些需要注意的地方及優(yōu)化思路和手段,希望能給大家?guī)?lái)一些啟發(fā)和幫助。
2.京東 jason
基本使用下面是Image的基本使用方法,image參數(shù)是Image控件中的必選參數(shù),它也是圖片數(shù)據(jù)的來(lái)源,可以是Asset、網(wǎng)絡(luò)、文件、內(nèi)存,下面將以我們常用的網(wǎng)絡(luò)圖片加載方式為例子講解原理,基本使用如下:
3.auto.js 京東
Image(image: Net白玉髓的寓意是什么workImage("https://avatars2.githubusercontent.com/u/20411648?s=460&v=4"),width: 100.0,
4.京東nlp
heitht: 100.0)
5.京東ui
文章篇幅有限Image控件的使用方法這里就不在展開(kāi)講解了,控件詳情及API方法請(qǐng)參閱:https://api.flutter.dev/flutter/widgets/Image-class.html圖片加載流程
6.github action 京東
Flutter 的圖片加載原理與原生客戶端中的圖片框架加載原理相似,具體可點(diǎn)擊下方大圖查看,加載步驟如下:1、 區(qū)分?jǐn)?shù)據(jù)來(lái)源生成白玉髓的寓意是什么緩存列表中數(shù)據(jù)映射的唯一key;2、 通過(guò)key讀取緩存列表中的圖片數(shù)據(jù);
7.京東git
3、 緩存存在,返回已存在的圖片數(shù)據(jù);4、 緩存不存在,按來(lái)源加載圖片數(shù)據(jù),解碼后同步到緩存中并返回;5、 設(shè)置回調(diào)監(jiān)聽(tīng)圖片數(shù)據(jù)加載狀態(tài),數(shù)據(jù)加載完成后重新渲染控件顯示圖片;
8.打開(kāi)京東 app ,購(gòu)物更輕松
大家可能注意到了上面流程圖中的文件緩存部分是灰色的,目前官方還不支持此功能,下面我們會(huì)通過(guò)源碼逐步分析加載流程及如何通過(guò)修改源碼補(bǔ)全文件緩存功能源碼分析下面將通過(guò)流程圖結(jié)合UML類圖分析圖片加載流程:
9.京東doga
這個(gè)UML類圖起初一看會(huì)感覺(jué)稍微有點(diǎn)兒復(fù)雜,但仔細(xì)看會(huì)發(fā)現(xiàn)已將圖片數(shù)據(jù)加載流程分成幾大模白玉髓的寓意是什么塊,下面將按照模塊進(jìn)行逐步分析,下面將以網(wǎng)絡(luò)圖片加載方式為例講解核心類和核心方法功能核心類及方法介紹01啟動(dòng)緩存相關(guān)類。
10.我想問(wèn)一下京東
PaintingBinding:圖片緩存類和著色器預(yù)加載,該類是基于框架的應(yīng)用程序啟動(dòng)時(shí)綁定到Flutter引擎的膠水類,在啟動(dòng)入口main.dart的runApp方法中創(chuàng)建WidgetsFlutterBinding類時(shí)被初始化的,通過(guò)覆蓋父類的initInstances()方法初始化內(nèi)部的著色器預(yù)加載(Skia第一次在GPU上繪制需要編譯相應(yīng)的著色器,這個(gè)過(guò)程大概20ms?200ms)及圖片緩存等,圖片緩存以單例的方式(PaintingBinding.i白玉髓的寓意是什么nstance.imageCache)對(duì)外提供方法使用,也就是說(shuō)這個(gè)圖片緩存在APP中是全局的,并在這個(gè)類中還提供了圖像解碼(instantiateImageCodec)、緩存清除(evict)等功能。
ImageCache:圖片緩存類,默認(rèn)提供緩存最大個(gè)數(shù)限制1000個(gè)對(duì)象和最大容量限制100MB,由于圖片加載過(guò)程是一個(gè)異步操作,所以緩存的圖片分為三種狀態(tài):已使用、已加載、未使用,分別對(duì)應(yīng)三個(gè)圖片緩存列表,當(dāng)圖片列表超限時(shí)會(huì)將圖片緩存列表中最近最少使用圖片進(jìn)行刪除,緩存列表分別是:已使用圖片緩存列表(_cache)、已加載圖片緩存列表(_pendingImages)、未使用圖片緩存列表(_li白玉髓的寓意是什么veImages),并對(duì)外提供以下方法:獲取緩存(putIfAbsent)、清空緩存(clear、clearLiveImages)、驅(qū)逐單個(gè)圖片(evict)、最大緩存?zhèn)€數(shù)限制(maximumSize)、最大緩存大小限制(maximumSizeBytes)等方法。
從源碼中我們可以看到緩存列表是Map類型,F(xiàn)lutter中的Map創(chuàng)建的對(duì)象是LinkedHashMap是有序的,按鍵值插入順序迭代,F(xiàn)lutter使用LinkedHashMap存儲(chǔ)圖片數(shù)據(jù)并實(shí)現(xiàn)類似LRU算法的緩存,當(dāng)緩存列表中的圖片被使用后會(huì)將圖片數(shù)據(jù)重新插入到緩存列表的末尾,這樣最近最少使用的圖片始終會(huì)被放在列表的頭部。
當(dāng)緩存列白玉髓的寓意是什么表增加圖片數(shù)據(jù)后,會(huì)通過(guò)最大緩存?zhèn)€數(shù)和最大緩存大小兩個(gè)緯度進(jìn)行檢查緩存列表是否超限,若存在超限情況則通過(guò)Map的keys.first方法獲取緩存列表頭部最近最少使用的圖片對(duì)象進(jìn)行刪除,直到滿足緩存限制。
啟動(dòng)緩存小結(jié):Flutter啟動(dòng)后在PaintingBinding中創(chuàng)建ImageCache緩存,圖片緩存是全局的并以單例方式對(duì)外提供使用方法,緩存默認(rèn)最大個(gè)數(shù)限制1000個(gè)對(duì)象、最大容量限制100MB,緩存中的Map列表通過(guò)key/value方式存儲(chǔ)圖片信息,并通過(guò)keys.first方法實(shí)現(xiàn)的類似LRU算法管理圖片緩存列表,對(duì)外提供putIfAbsent()方法獲取已緩存圖像,若緩存中不存在白玉髓的寓意是什么則通過(guò)回調(diào)圖片加載類中的load()方法加載圖片數(shù)據(jù),另外圖片緩存中還提供clear()和evict()方法用來(lái)刪除緩存。
02圖片數(shù)據(jù)加載相關(guān)類ImageProvider:圖片數(shù)據(jù)提供抽象類,該類定義了圖像數(shù)據(jù)解析方法(resolve)、唯一key生成方法(obtainKey)、數(shù)據(jù)加載方法(load),obtainKey 和load方法均由子類實(shí)現(xiàn),obtainKey方法生成的對(duì)象用于內(nèi)存緩存的key值使用,load方法將按照不同數(shù)據(jù)源加載圖像數(shù)據(jù),常用的Provider子類有:NetworkImage、AssetImage、FileImage、MemoryImage,我們可以看到resol白玉髓的寓意是什么ve方法返回的是圖片加載對(duì)象類(ImageStream),load方法返回的是ImageStreamCompleter類用來(lái)管理圖像加載狀態(tài)及圖像數(shù)據(jù)(ImageInfo)。
ImageStreamCompleter:是一個(gè)抽象類,用于管理加載圖像對(duì)象(ImageInfo)加載過(guò)程的一些接口,Image控件中正是通過(guò)它來(lái)監(jiān)聽(tīng)圖片加載狀態(tài)的。
ImageStream:圖像的加載對(duì)象,可監(jiān)聽(tīng)圖像數(shù)據(jù)加載狀態(tài),由ImageStreamCompleter返回一個(gè)ImageInfo對(duì)象用于圖像顯示
NetworkImage:網(wǎng)絡(luò)圖片加載類,ImageProvider的實(shí)現(xiàn)類,通過(guò)URL加載網(wǎng)絡(luò)圖像,覆蓋loa白玉髓的寓意是什么d()方法返回ImageStreamCompleter的實(shí)現(xiàn)類MultiFrameImageStreamCompleter,構(gòu)建該類需要一個(gè)codec參數(shù)類型是Future,通過(guò)調(diào)用_loadAsync()方法下載網(wǎng)絡(luò)圖片數(shù)據(jù)獲得字節(jié)流后通過(guò)調(diào)用PaintingBinding.instance.instantiateImageCodec方法對(duì)數(shù)據(jù)進(jìn)行解碼后獲得Future對(duì)象,obtainKey方法我們發(fā)現(xiàn)返回的是SynchronousFuture(this)對(duì)象,正是NetworkImage 自己本身,我們通過(guò)該類的==方法可以看到判斷兩個(gè)NetworkImage類是否相等通過(guò)runtime白玉髓的寓意是什么Type 、url 、scale 這三個(gè)參數(shù)來(lái)判斷,所以圖片緩存中的key相等判斷取決于圖片的url、scale、runtimeType參數(shù)。
MultiFrameImageStreamCompleter:是ImageStreamCompleter的實(shí)現(xiàn)類是Flutter SDK的預(yù)置類,構(gòu)建該類需要一個(gè)codec參數(shù)類型是Future,Codec 是處理圖像編解碼器的句柄也是Flutter Engine API的包裝類,可通過(guò)其內(nèi)部的frameCount變量獲取圖像幀數(shù),分別處理單幀和多幀(動(dòng)態(tài)圖)圖像,內(nèi)部的getNextFrame()方法獲取每幀的圖像數(shù)據(jù)并創(chuàng)建Image控件中渲染需要的I白玉髓的寓意是什么mageInfo數(shù)據(jù),調(diào)用onImage方法將ImageInfo返回給Image控件。
圖像數(shù)據(jù)加載小結(jié):上面以網(wǎng)絡(luò)圖像加載流程分析,首先通過(guò)ImageProvider的resolve()方法創(chuàng)建ImageStream對(duì)象,obtainKey()方法創(chuàng)建圖像緩存列表中的唯一key(取決于圖像url和scale),通過(guò)load()方法加載圖像數(shù)據(jù)并返回MultiFrameImageStreamCompleter對(duì)象,并將其設(shè)置給ImageStream中的setCompleter()方法添加監(jiān)聽(tīng)圖像加載完成狀態(tài),圖像數(shù)據(jù)通過(guò)Codec 處理幀數(shù)分別處理最終創(chuàng)建ImageInfo對(duì)象通過(guò)ImageSt白玉髓的寓意是什么reamListener的onImage方法返回給Image控件。
03圖片渲染相關(guān)類_ImageState:是Image控件創(chuàng)建的State類,通過(guò)調(diào)用ImageProvider的resolve()方法解析圖片數(shù)據(jù),resolve()方法返回的ImageStream對(duì)象,通過(guò)addListener()增加圖片解析狀態(tài)監(jiān)聽(tīng),通過(guò)ImageStreamListener的onImage回調(diào)中獲取圖片數(shù)據(jù)(ImageInfo)加載完成狀態(tài),onChunk回調(diào)監(jiān)聽(tīng)數(shù)據(jù)加載進(jìn)度,onError監(jiān)聽(tīng)圖片加載錯(cuò)誤狀態(tài),最終通過(guò)調(diào)用setState進(jìn)行數(shù)據(jù)更新繪制。
細(xì)心的同學(xué)會(huì)發(fā)現(xiàn)ImageProvider的實(shí)白玉髓的寓意是什么例對(duì)象(widget.image)被ScrollAwareImageProvider包裝了一下又重新創(chuàng)建了一個(gè)provider,在ScrollAwareImageProvider內(nèi)部主要是重寫(xiě)了其中的resolveStreamForKey()方法,F(xiàn)lutter SDK 1.17版本中對(duì)圖片解析增加了快速滾動(dòng)優(yōu)化,當(dāng)判斷當(dāng)前屏幕處在快速滾動(dòng)狀態(tài)時(shí),則將圖片解析過(guò)程延遲下一幀幀尾進(jìn)行。
RawImage:RenderObjectWidget的子類,重寫(xiě)createRenderObject方法創(chuàng)建RenderObject子類RenderImage:渲染樹(shù)中RenderObject的實(shí)現(xiàn)類,F(xiàn)lutt白玉髓的寓意是什么er的三棵樹(shù)Widget、Element、RenderObject ,而RenderObject是負(fù)責(zé)繪制渲染的,RenderImage重寫(xiě)performLayout()方法度量渲染尺寸并布局,重寫(xiě)paint()方法獲取畫(huà)布Canvas,Canvas是記錄圖片操作的接口類,通過(guò)參數(shù)處理圖片鏡像、裁剪、平鋪等邏輯后調(diào)用的drawImageNine()和drawImageRect()方法將圖片合成到畫(huà)布上最終調(diào)用Skia引擎API進(jìn)行繪制。
圖片渲染小結(jié):Image控件中通過(guò)調(diào)用ImageProvider的resolve()方法獲取圖片數(shù)據(jù)ImageInfo對(duì)象,通過(guò)setState方法將數(shù)據(jù)更新給白玉髓的寓意是什么圖片渲染控件(RenderImage),RenderImage中重寫(xiě)paint()方法根據(jù)傳入?yún)?shù)對(duì)圖片數(shù)據(jù)處理后繪制到Canvas畫(huà)布上并調(diào)用Skia引擎API進(jìn)行繪制。
總結(jié)經(jīng)過(guò)以上源碼探索我們發(fā)現(xiàn)Flutter本身提供了定制化的內(nèi)存緩存能力,但內(nèi)存緩存上限默認(rèn)是100MB,這樣在配置比較低的機(jī)器上內(nèi)存(Flutter+原生)會(huì)超出上限產(chǎn)生OOM,所以使用中我們需要獲取機(jī)器的實(shí)際物理內(nèi)存去重新調(diào)整Flutter端的內(nèi)存緩存限制大小,通過(guò)PaintingBinding.instance.imageCache調(diào)用的maximumSize和maximumSizeBytes動(dòng)態(tài)設(shè)置合理的圖片緩存限制白玉髓的寓意是什么避免因圖片內(nèi)存占用過(guò)多導(dǎo)致OOM,那么我們?cè)诜喠薎mage源碼后還能做些什么呢?請(qǐng)繼續(xù)往下看。
未顯示圖像內(nèi)存優(yōu)化:在使用過(guò)程中我們發(fā)現(xiàn)部分離開(kāi)屏幕的圖片始終被保存在內(nèi)存緩存中,結(jié)合Image控件生命周期中的deactive()、dispose()等方法,在頁(yè)面控件中的圖片未顯示在屏幕上或控件已銷毀時(shí)調(diào)用圖片緩存中的evict()方法進(jìn)行資源釋放減少內(nèi)存開(kāi)銷。
圖片預(yù)緩存處理:Image控件中提供了precacheImage()方法可以將需要顯示的圖片預(yù)先加載到ImageCache的緩存列表中,緩存列表中通過(guò)key值區(qū)分相同圖片,在頁(yè)面打開(kāi)后直接從內(nèi)存緩存獲取,可快速顯示圖片。
圖片文件磁盤緩存:白玉髓的寓意是什么通過(guò)查看網(wǎng)絡(luò)圖片加載類NetworkImage源碼可以發(fā)現(xiàn),圖片數(shù)據(jù)下載和解碼過(guò)程都是通過(guò)_loadAsync()方法完成的,所以我們可以通過(guò)改造這個(gè)方法中圖片文件下載、讀取、保存過(guò)程去增加圖片文件本地存儲(chǔ)、打通原生圖片庫(kù)緩存、圖片下載DNS處理等功能。
自定義占位圖、錯(cuò)誤圖效果:
Image控件中的frameBuilder和errorBuilder參數(shù)分別為我們提供了占位圖和錯(cuò)誤圖的自定義方式,也可使用FadeInImage控件提供的占位圖(placeholder)、錯(cuò)誤圖imageErrorBuilder等參數(shù),F(xiàn)adeInImage內(nèi)部實(shí)現(xiàn)也是Image控件,感興趣的同學(xué)可以查看其源碼實(shí)現(xiàn)。白玉髓的寓意是什么
大圖下載進(jìn)度自定義顯示:
顯示效果:https://flutter.github.io/assets-for-api-docs/assets/widgets/loading_progress_image.mp4圖片可拉伸區(qū)域設(shè)置(.9圖):
RenderImage的paint方法中我們發(fā)現(xiàn)在調(diào)用Canvas API繪制前會(huì)判斷centerSlice參數(shù)分別調(diào)用drawImageNine()和drawImageRect()方法,Image正式通過(guò)centerSlice參數(shù)配置圖片的可拉伸區(qū)域,參考代碼:centerSlice: Rect.fromLTWH(20, 20, 1, 1),L:橫向可拉伸區(qū)白玉髓的寓意是什么域左邊起始點(diǎn)位置,T:縱向可拉伸區(qū)域上邊起始點(diǎn)位置,W:橫向可拉伸區(qū)域?qū)挾?,H:縱向可拉伸區(qū)域?qū)挾取?/p>
未來(lái)規(guī)劃本文介紹了京東APP中Flutter探索遇到的問(wèn)題以及圖片的加載原理和使用過(guò)程中的一些技巧,隨著Flutter SDK版本迭代更新,我們將繼續(xù)對(duì)圖片加載框架進(jìn)行優(yōu)化,原生開(kāi)發(fā)中的多個(gè)優(yōu)秀圖片框架已經(jīng)經(jīng)歷了大量用戶的考驗(yàn)這也一直是我們渴望在Flutter上復(fù)用的能力,所以我們也在積極探索原生和Flutter中圖片內(nèi)存共享方案,我們希望這個(gè)增強(qiáng)能力是非侵入式的,我們也在嘗試外接紋理等方案,這塊技術(shù)細(xì)節(jié)進(jìn)展將在后續(xù)文章中繼續(xù)和大家一起探討。
參考資料1、http://storage.360buy白玉髓的寓意是什么img.com/pub-image/Image-source.jpg2、https://book.flutterchina.club/chapter14/image_and_cache.html
3、https://api.flutter-io.cn/flutter/painting/ImageCache-class.html