国产亚洲欧美人成在线,免费视频爱爱太爽了无码,日本免费一区二区三区高清视频 ,国产真实伦对白精彩视频

歡迎您光臨深圳塔燈網(wǎng)絡(luò)科技有限公司!
電話圖標(biāo) 余先生:13699882642

網(wǎng)站百科

為您解碼網(wǎng)站建設(shè)的點(diǎn)點(diǎn)滴滴

Flutter之禪 內(nèi)存優(yōu)化篇

發(fā)表日期:2018-10 文章編輯:小燈 瀏覽次數(shù):7266

Flutter之禪 內(nèi)存優(yōu)化篇

前言

性能穩(wěn)定性是App的生命,F(xiàn)lutter帶了很多創(chuàng)新與機(jī)遇,然而團(tuán)隊在享受Flutter帶來的收益同時也迎接了很多新事物帶來的挑戰(zhàn)。

本文就內(nèi)存優(yōu)化過程中一些實(shí)踐經(jīng)驗(yàn)跟大家做一個分享。

Flutter 上線之后

閑魚使用一套混合棧管理的方案將Flutter嵌入到現(xiàn)有的App中。在產(chǎn)品體驗(yàn)上我們?nèi)〉昧藘?yōu)于Native的體驗(yàn)。主要得益于Flutter的在跨平臺渲染方面的優(yōu)勢,部分原因則是因?yàn)槲覀冇肈art語言重新實(shí)現(xiàn)的頁面拋棄了很多歷史的包袱輕裝上陣。

上線之后各方面技術(shù)指標(biāo),都達(dá)到甚至超出了部分預(yù)期。而我們最為擔(dān)心的一些穩(wěn)定性指標(biāo),比如crash也在穩(wěn)定的范圍之內(nèi)。但是在一段時間后我們發(fā)現(xiàn)由于內(nèi)存過高而被系統(tǒng)殺死的abort率數(shù)據(jù)有比較明顯的異常。性能穩(wěn)定性問題是非常關(guān)鍵的,于是我們火速開展了問題排查。

問題定位與排查

顯然問題出在了過大的內(nèi)存消耗上。內(nèi)存消耗在App中構(gòu)成比較復(fù)雜,如何在復(fù)雜的業(yè)務(wù)中去定位到罪魁禍?zhǔn)啄??稍加觀察,我們確定Flutter問題相對比價明顯。工欲善其事必先利其器,需要更好地定位內(nèi)存的問題,善用已經(jīng)的工具是非常有幫助的。好在我們在Native層和Dart層都有足夠多的性能分析工具進(jìn)行使用。

工具分析

這里簡單介紹我們?nèi)绾问褂玫墓ぞ呷ビ^察手機(jī)數(shù)據(jù)以便于分析問題。需要注意的是,本文的重點(diǎn)不是工具的使用方法介紹,所以只是簡單列舉部分使用到的常見工具。

Xcode Instruments

Instruments是iOS內(nèi)存排查的利器,可以比較便捷地觀察實(shí)時內(nèi)存使用情況,自然不必多說。

Xcode MemGraph + VMMap

XCode 8之后推出的MEMGraph是Xcode的內(nèi)存調(diào)試?yán)?,可以看到?shí)時的可視化的內(nèi)存。更為方便的是,你可以將MemGraph導(dǎo)出,配合命令行工具更好的得到結(jié)構(gòu)化的信息。

Dart Observatory

這是Dart語言官方的調(diào)試工具,里面也包含了類似于Xcode的Instruments的工具。在Debug模式下Dart VM啟動以后會在特定的端口接受調(diào)試請求。官方文檔

觀察結(jié)果

在整個過程中我進(jìn)行了大量的觀察,這里分享一部分典型的數(shù)據(jù)表現(xiàn)。

通過Xcode Instruments排查的話,我們觀察到CG Raster Data這個數(shù)據(jù)有些高。這個Raster Data呢其實(shí)是圖片光柵化的時候的內(nèi)存消耗。

我們將App內(nèi)存異常的場景的MemGraph導(dǎo)出來,對其執(zhí)行VMMap指令得出的結(jié)果:

vmmap --summary Runner[40957].memgraphvmmap Runner[40957].memgraph | grep 'IOKit' 
vmmap Summary vmmap address

我們主要關(guān)注resident和dirty的內(nèi)存。發(fā)現(xiàn)IOKit占用了大量的內(nèi)存。

結(jié)合Xcode Raster Data還有IOKit的大量內(nèi)存消耗,我們開始懷疑問題是圖內(nèi)存泄漏導(dǎo)致的。經(jīng)過進(jìn)一步通過Dart Observatory觀察Dart Image對象的內(nèi)存情況。


Dart image instance

觀察結(jié)果顯示,在內(nèi)存較高的場景下在Dart層的確同時存在了較多Image(如圖中270)的對象。現(xiàn)在基本可以確定內(nèi)存問題跟Dart層的圖片有很大的關(guān)系。

這個結(jié)果,我估計很多人都已經(jīng)想到了,App有明顯的內(nèi)存問題很有可能就是跟多媒體資源有關(guān)系。通過工具得出的準(zhǔn)確數(shù)據(jù)線索,我們得到一個大致的方向去深入研究。

詭異的Dart圖片數(shù)量爆炸

圖片對象泄漏?

前面我們用工具觀察到Dart層的Image對象數(shù)量過多直接導(dǎo)致了非常大的內(nèi)存壓力,我們起初懷疑存在圖片的內(nèi)存泄漏。但是我們在經(jīng)過進(jìn)一步確認(rèn)以后發(fā)現(xiàn)圖片其實(shí)并沒有真正的泄漏。

Dart語言采用垃圾回收機(jī)制(Garbage Collection 下面開始簡稱GC)來管理分配的內(nèi)存,VM層面的垃圾回收應(yīng)該大多數(shù)情況下是可信的。但是從實(shí)際觀察來看,圖片數(shù)量的爆炸造成的較大的內(nèi)存峰值直觀感覺上GC來得有些不及時。在Debug模式下我們使用Dart Observatory手動觸發(fā)GC,最終這些圖片對象在沒有引用的情況下最終還是會被回收。

至此,我們基本可以確認(rèn),圖片對象不存在泄漏。那是什么導(dǎo)致了GC的反應(yīng)遲鈍呢,難道是Dart語言本身的問題嗎?

Garbage Collection 不及時?

為此我需要了解一下Dart內(nèi)存管理機(jī)制垃圾回收的實(shí)現(xiàn),關(guān)于詳細(xì)的內(nèi)存問題我團(tuán)隊的 @匠修 同學(xué)已經(jīng)發(fā)過一篇相關(guān)文章可以參考:內(nèi)存文章

我這里不詳細(xì)討論Dart垃圾回收實(shí)現(xiàn)細(xì)節(jié),只聊一聊Flutter與Dart相關(guān)的一些內(nèi)容。
[圖片上傳失敗...(image-8cfc73-1539229685285)]
關(guān)于Flutter我需要首先明確幾個概念:

  1. Framework(Dart)(跟iOS平臺連接的庫Flutter.framework要區(qū)別開)特指由Dart編寫的Flutter相關(guān)代碼。

  2. Dart VM執(zhí)行Dart代碼的Dart語言相關(guān)庫,它是以C實(shí)現(xiàn)的Dart SDk形式提供的。對外主要暴露了C接口Dart Api。里面主要包含了Dart的編譯器,運(yùn)行時等等。

  3. FLutter Engine C++實(shí)現(xiàn)的Flutter驅(qū)動引擎。他主要負(fù)責(zé)跨平臺的繪制實(shí)現(xiàn),包含Skia渲染引擎的接入;Dart語言的集成;以及跟Native層的適配和Embeder相關(guān)的一些代碼。簡單理解,iOS平臺上面Flutter.framework, Android平臺上的Flutter.jar便是引擎代碼構(gòu)建后的產(chǎn)物。

在Dart代碼里面對于GC是沒有感知的。

對于Dart SDK也就是Dart語言我們可以做的很有限,因?yàn)镈art語言本身是一種標(biāo)準(zhǔn),如果Dart真的有問題我們需要和Dart維護(hù)團(tuán)隊協(xié)作推進(jìn)問題的解決。Dart語言設(shè)計的時候初衷也是希望GC對于使用者是透明的,我們不應(yīng)該依賴GC實(shí)現(xiàn)的具體算法和策略。不過我們還是需要通過Dart SDK的源碼去理解GC的大致情況。

既然我們前面已經(jīng)確認(rèn)并非內(nèi)存泄漏,所以我們在對GC延遲的問題的調(diào)查主要放在Flutter Engine以及Dart CG入口上。

Flutter與Dart Garbage Collection

既然感覺GC不及時,先撇開消耗,我們至少可以嘗試多觸發(fā)幾次GC來減輕內(nèi)存峰值壓力。但是我在仔細(xì)查閱dart_api.h(/src/third_party/dart/runtime/include/dart_api.h )接口文件后,但是并沒有找到顯式提供觸發(fā)GC的接口。

但是找到了如下這個方法Dart_NotifyIdle

/*** Notifies the VM that the embedder expects to be idle until |deadline|. The VM* may use this time to perform garbage collection or other tasks to avoid* delays during execution of Dart code in the future.** |deadline| is measured in microseconds against the system's monotonic time.* This clock can be accessed via Dart_TimelineGetMicros().** Requires there to be a current isolate.*/ DART_EXPORT void Dart_NotifyIdle(int64_t deadline); 

這個接口意思是我們可以在空閑的時候顯式地通知Dart,你接下來可以利用這些時間(dealine之前)去做GC。注意,這里的GC不保證會馬上執(zhí)行,可以理解我們請求Dart去做GC,具體做不做還是取決于Dart本身的策略。

另外,我還找到一個方法叫做Dart_NotifyLowMemory:

/*** Notifies the VM that the system is running low on memory.** Does not require a current isolate. Only valid after calling Dart_Initialize.*/ DART_EXPORT void Dart_NotifyLowMemory(); 

不過這個Dart_NotifyLowMemory方法其實(shí)跟GC沒有太大關(guān)系,它其實(shí)是在低內(nèi)存的情況下把多余的isolate去終止掉。你可以簡單理解,把一些不是必須的線程給清理掉。

在研究Flutter Engine代碼后你會發(fā)現(xiàn),F(xiàn)lutter Engine其實(shí)就是通過Dart_NotifyIdle去跟Dart層進(jìn)行GC方面的協(xié)作的。我們可以在Flutter Engine源碼animator.cc看到以下代碼:

 //Animator負(fù)責(zé)刷新和通知幀的繪制 if (!frame_scheduled_) { // We don't have another frame pending, so we're waiting on user input // or I/O. Allow the Dart VM 100 ms. delegate_.OnAnimatorNotifyIdle(*this, dart_frame_deadline_ + 100000); } //delegate 最終會調(diào)用到這里 bool RuntimeController::NotifyIdle(int64_t deadline) { if (!root_isolate_) { return false; }tonic::DartState::Scope scope(root_isolate_.get()); //Dart api接口 Dart_NotifyIdle(deadline); return true; }

這里的邏輯比較直觀:如果當(dāng)前沒有幀渲染的任務(wù)時候就通過NotifyIdle告訴Dart層可以進(jìn)行GC操作了。注意,這里并不是說只有在這種情況下Dart才回去做GC,F(xiàn)lutter只是通過這種方式盡可能利用空閑去做GC,配合Dart以更合理的時間去做GC。

看到這里,我們有足夠的理由去嘗試一下這個接口,于是我們在一些內(nèi)存壓力比較大的場景進(jìn)行了手動請求GC的操作。線上的Abort雖然有明顯好轉(zhuǎn),但是內(nèi)存峰值并沒有因此得到改善。我們需要進(jìn)一步找到根本原因。

圖片數(shù)量爆炸的真相

為了確定圖片大量囤積釋放不及時的問題,我們需要跟蹤Flutter圖片從初始化到銷毀的整個流程。

我們從Dart層開始去追尋Image對象的生命周期,我們可以看到Flutter里面所以的圖片都是經(jīng)過ImageProvider來獲取的,ImageProvider在獲取圖片的時候會調(diào)用一個Resolve接口,而這個接口會首先查詢ImageCache去讀取圖片,如果不存在緩存就new Image的實(shí)例出來。

關(guān)鍵代碼:

ImageStream resolve(ImageConfiguration configuration) { assert(configuration != null); final ImageStream stream = new ImageStream(); T obtainedKey; obtainKey(configuration).then<void>((T key) { obtainedKey = key; stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key))); }).catchError( (dynamic exception, StackTrace stack) async { FlutterError.reportError(new FlutterErrorDetails( exception: exception, stack: stack, library: 'services library', context: 'while resolving an image', silent: true, // could be a network error or whatnot informationCollector: (StringBuffer information) { information.writeln('Image provider: $this'); information.writeln('Image configuration: $configuration'); if (obtainedKey != null) information.writeln('Image key: $obtainedKey'); } )); return null; } ); return stream; } 

大致的邏輯

  1. Resolve 請求獲取圖片.
  2. 查詢是否存在于ImageCache.Yes->3 NO->4
  3. 返回已經(jīng)存在的圖片對象
  4. 生成新的Image對象并開始加載
    看起來沒有特別復(fù)雜的邏輯,不過這里我要提一下Flutter ImageCache的實(shí)現(xiàn)。

Flutter ImageCache

Flutter ImageCache最初的版本其實(shí)非常簡單,用Map實(shí)現(xiàn)的基于LRU算法緩存。這個算法和實(shí)現(xiàn)沒有什么問題,但是要注意的是ImageCache緩存的是ImageStream對象,也就是緩存的是一個異步加載的圖片的對象。而且緩存沒有對占用內(nèi)存總量做限制,而是采用默認(rèn)最大限制1000個對象(Flutter在0.5.6 beta中加入了對內(nèi)存大小限制的邏輯)。緩存異步加載對象的一個問題是,在圖片加載解碼完成之前,無法知道到底將要消耗多少內(nèi)存,至少在Flutter這個Cache實(shí)現(xiàn)中沒有處理這個問題。具體的實(shí)現(xiàn)感興趣的朋友可以閱讀ImageCache.dart源碼。

其實(shí)Flutter本身提供了定制化Cache的能力,所以優(yōu)化ImageCache的第一步就是要根據(jù)機(jī)型的物理內(nèi)存去做緩存大小的適配,設(shè)置ImageCache的合理限制。關(guān)于ImageCache的問題,可以參考官方文檔和這個issue,我這里不展開去聊了。

Flutter Image生命周期

回到我們的Image對象跟蹤,很明顯,在緩存沒有命中的情況下會有新的Image產(chǎn)生。繼續(xù)深入代碼會發(fā)現(xiàn)Image對象是由這段代碼產(chǎn)生的:

 Future<Codec> instantiateImageCodec(Uint8List list) { return _futurize( (_Callback<Codec> callback) => _instantiateImageCodec(list, callback, null) ); }String _instantiateImageCodec(Uint8List list, _Callback<Codec> callback, _ImageInfo imageInfo) native 'instantiateImageCodec'; 

這里有個native關(guān)鍵字,這是Dart調(diào)用C代碼的能力,我們查看具體的源碼可以發(fā)現(xiàn)這個最終初始化的是一個C++的codec對象。具體的代碼在Flutter Engine codec.cc。它大致的過程就是先在IO線程中啟動了一個解碼任務(wù),在IO完成之后再把最終的圖片對象發(fā)回UI線程。關(guān)于Flutter線程的詳細(xì)介紹,我在另外一篇文章中已經(jīng)有介紹,這里附上鏈接給有興趣的朋友。深入理解Flutter Engine線程模型。經(jīng)過來這些代碼和線程分析,我們得到大致的流程圖:

圖片爆炸流程圖

也就是說,解碼任務(wù)在IO線程進(jìn)行,IO任務(wù)隊列里面都是C++ lambda表達(dá)式,持有了實(shí)際的解碼對象,也就持有了內(nèi)存資源。當(dāng)IO線程任務(wù)過多的時候,會有很多IO任務(wù)在等待執(zhí)行,這些內(nèi)存資源也被閉包所持有而等待釋放。這就是為什么直觀上會有內(nèi)存釋放不及時而造成內(nèi)存峰值的問題。這也解釋了為什么之前拿到的vmmap虛擬內(nèi)存數(shù)據(jù)里面IOKit是大頭。

這樣我們找到了關(guān)鍵的線索,在緩存不命中的情況下,大量初始化Image對象,導(dǎo)致IO線程任務(wù)繁重,而IO又持有大量的圖片解碼所用的內(nèi)存資源。帶這個推論,我在Flutter Engine的Task Runner加入了任務(wù)數(shù)量和C++ image對象的監(jiān)控代碼,證實(shí)了的確存在IO任務(wù)線程過載的情況,峰值在極端情況下瞬時達(dá)到了100+IO操作。

IO Runner監(jiān)控

到這里問題似乎越來越明了了,但是為什么會有這么IO任務(wù)觸發(fā)呢?上述邏輯雖然可能會有IO線程過載的情況下占用大量內(nèi)存的情況。上層要求生成新的圖片對象,這種請求是沒有錯誤的,設(shè)計就是如此。就好比主線程阻塞大量的任務(wù),必然會導(dǎo)致界面卡頓,但者卻不是主線程本身的問題。我們需要從源頭找到導(dǎo)致新對象創(chuàng)建暴漲真正導(dǎo)致IO線程過載的原因。

大量請求的根源

在前面的線索之下,我們繼續(xù)尋找問題的根源。我們在實(shí)際App操作的過程當(dāng)中發(fā)現(xiàn),頁面Push的越多,圖片生成的速度越來越快。也就是說頁面越多請求越快,看起來沒有什么大問題。但是可見的圖片其實(shí)總是在一定數(shù)量范圍之內(nèi)的,不應(yīng)該隨著頁面增多而加快對象創(chuàng)建的頻率。我們下意識的開始懷疑是否存在不可見的Image Widget也在不斷請求圖片的情況。最終導(dǎo)致了Cache無法命中而大量生成新的圖片的場景。

我開始調(diào)查每個頁面的圖片加載請求,我們知道Flutter里面萬物皆Widget,頁面都是是Widget,由Navigator管理。我在Widget的生命周期方法(詳細(xì)見Flutter官方文檔)中加入監(jiān)控代碼,如我所料,在Navigator棧底下不可見的頁面也還在不停的Resolve Image,直接導(dǎo)致了image對象暴漲而導(dǎo)致IO線程過載,導(dǎo)致了內(nèi)存峰值。

看起來,我們終于找到了根本原因。解決方案并不難。在頁面不可見的時候沒必要發(fā)出多余的圖片加載請求,峰值也就隨之降下來了。再經(jīng)過一番代碼優(yōu)化和測試以后問題得到了根本上的解決。優(yōu)化上線以后,我們看到了數(shù)據(jù)發(fā)生了質(zhì)的好轉(zhuǎn)。
有朋友可能想問,為什么不可見的Widget也會被調(diào)用到相關(guān)的生命周期方法。這里我推薦閱讀Flutter官方文檔關(guān)于Widget相關(guān)的介紹,篇幅有限我這里不展開介紹了。widgets

至此,我們已經(jīng)解決了一個較為嚴(yán)重的內(nèi)存問題。內(nèi)存優(yōu)化情況復(fù)雜,可以點(diǎn)也比較多,接下來我繼續(xù)簡要分享在其它一些方面的優(yōu)化方案。

截圖緩存優(yōu)化

文件緩存+預(yù)加載策略

我們是采用嵌入式Flutter并使用一套混合棧模式管理Native和Flutter頁面相互跳轉(zhuǎn)的邏輯。由于FlutterView在App中是單例形式存在的,我們?yōu)榱烁玫挠脩趔w驗(yàn),在頁面切換的過程中使用的截圖的方式來進(jìn)行過渡。

大家都知道,圖片是非常占用內(nèi)存的對象,我們?nèi)绾卧诓唤档陀脩趔w驗(yàn)的同時獲得最小的內(nèi)存消耗呢?假如我們每push一個頁面都保存一張截圖,那么內(nèi)存是以線性復(fù)雜度增長的,這顯然不夠好。

內(nèi)存和空間在大多數(shù)情況下是一個互相轉(zhuǎn)換的關(guān)系,優(yōu)化很多時候其實(shí)是找一個合理的折中點(diǎn)。
最終我采用了預(yù)加載+緩存的策略,在頁面最多只在內(nèi)存中同時存在兩個截圖,其它的存文件,在需要的時候提前進(jìn)行預(yù)加載。
簡要流程圖:

簡要流程圖

這樣的話就做到了不影響用戶體驗(yàn)的前提下,將空間復(fù)雜度從O(n)降低到了O(1)。
這個優(yōu)化進(jìn)一步節(jié)省了不必要的內(nèi)存開銷。

截圖額外的優(yōu)化

  • 針對當(dāng)前設(shè)備的內(nèi)存情況,自適應(yīng)調(diào)整截圖的分辨率,爭取最小的內(nèi)存消耗。
  • 在極端的內(nèi)存情況下,把所有截圖都從內(nèi)存中移除存(存文件可恢復(fù)),采用PlaceHolder的形式。極端情況下避免被殺,保證可用性的體驗(yàn)降級策略。

頁面兜底策略

對于電商類App存在一個普遍的問題,用戶會不斷的push頁面到棧里面,我們不能阻止用戶這種行為。我們當(dāng)然可以把老頁面干掉,每次回退的時候重新加載,但是這種用戶體驗(yàn)跟Web頁一樣,是用戶不可接受的。我們要維持頁面的狀態(tài)以保證用戶體驗(yàn)。這必然會導(dǎo)致內(nèi)存的線性增長,最終肯定難免要被殺。我們優(yōu)化的目的是提高用戶能夠push的極限頁面數(shù)量。

對于Flutter頁面優(yōu)化,除了在優(yōu)化每一個頁面消耗的內(nèi)存之外,我們做了降級兜底策略去保證App的可用性:在極端情況下將老頁面進(jìn)行銷毀,在需要的時候重新創(chuàng)建。這的確降低了用戶體驗(yàn),在極端情況下,降級體驗(yàn)還是比Crash要好一些。

image

FlutterViewController 單例析構(gòu)

另外我想討論的一個話題是關(guān)于FlutterViewController的。目前Flutter的設(shè)計是按照單例模式去運(yùn)行的,這對于完全用Flutterc重新開發(fā)的App沒有太大的問題。但是對于混合型App,多出來的常駐內(nèi)存確實(shí)是一個問題。

實(shí)際上,F(xiàn)lutter Engine底層實(shí)現(xiàn)是考慮到了析構(gòu)這個問題,有相關(guān)的接口。但是在Embeder這一層(具體FlutterViewController Message Channels這一層),在實(shí)現(xiàn)過程中存在一些循環(huán)引用,導(dǎo)致在Native層就算沒有引用FlutterViewController的時候也無法釋放.

FlutterViewController引用圖

我在經(jīng)過一段時間的嘗試后,算是把循環(huán)引用解除了。這些循環(huán)引用主要集中在FlutterChannel這一塊。在解除之后我順利的釋放了FlutterViewController,可以明顯看到常駐內(nèi)存得到了釋放。但是我發(fā)現(xiàn)釋放FlutterViewController的時候會導(dǎo)致一部分Skia Image對象泄漏,因?yàn)镾kia Objects必須在它創(chuàng)建的線程進(jìn)行釋放(詳情請參考skia_gpu_object.cc源碼),線程同步的問題。關(guān)于這個問題我在GitHub上面有一個issue大家可以參考。FlutterViewController釋放issue

目前,這個優(yōu)化我們已經(jīng)反饋給Flutter團(tuán)隊,期待他們官方支持。希望大家可以一起探索研究。

進(jìn)一步探討

除此之外,F(xiàn)lutter內(nèi)存方面其實(shí)還有比較多方面可以去研究。我這里列舉幾個目前觀察到的問題。

  1. 我在內(nèi)存分析的時候發(fā)現(xiàn)Flutter底層使用的boring ssl庫有可以確定的內(nèi)存泄漏。雖然這個泄漏比較緩慢,但是對于App長期運(yùn)行還是有影響的。我在GitHub上面提了個issue跟進(jìn),目前已有相關(guān)的人員進(jìn)行跟進(jìn)。SSL leak issue

  2. 關(guān)于圖片渲染,目前Flutter還是有優(yōu)化空間的,特別是圖片的按需剪裁。大多數(shù)情況下是沒有不要將整一個bitmap解壓到內(nèi)存中的,我們可以針對顯示的區(qū)域大小和屏幕的分辨率對圖片進(jìn)行合理的縮放以取得最好的性能消耗。

  3. 在分析Flutter內(nèi)存的MemGraph的時候,我發(fā)現(xiàn)Skia引擎當(dāng)中對于TextLayout消耗了大量的內(nèi)存.目前我沒有找到具體的原因,可能存在優(yōu)化的空間。

結(jié)語

在這篇文章里,我簡要的聊了一下目前團(tuán)隊在Flutter應(yīng)用內(nèi)存方面做出的嘗試和探索。短短一篇文章無法包含所有內(nèi)容,只能推出了幾個典型的案例來作分析,希望可以跟大家一起探討研究。歡迎感興趣的朋友一起研究,如有更好的想法方案,我非常樂意看到你的分享。

閑魚期待你的加入

歡迎加入閑魚,一起探索Flutter更多可能。
簡歷投遞:guicai.gxy@alibaba-inc.com

參考資料

  • (https://github.com/flutter/flutter)Flutter 開發(fā)文檔
  • (https://github.com/flutter/engine)Flutter 引擎文檔
  • (https://flutter.io/)Flutter IO
  • (https://www.dartlang.org/)Dart 語言開發(fā)文檔

本頁內(nèi)容由塔燈網(wǎng)絡(luò)科技有限公司通過網(wǎng)絡(luò)收集編輯所得,所有資料僅供用戶學(xué)習(xí)參考,本站不擁有所有權(quán),如您認(rèn)為本網(wǎng)頁中由涉嫌抄襲的內(nèi)容,請及時與我們聯(lián)系,并提供相關(guān)證據(jù),工作人員會在5工作日內(nèi)聯(lián)系您,一經(jīng)查實(shí),本站立刻刪除侵權(quán)內(nèi)容。本文鏈接:http://jstctz.cn/18406.html
相關(guān)APP開發(fā)
 八年  行業(yè)經(jīng)驗(yàn)

多一份參考,總有益處

聯(lián)系深圳網(wǎng)站公司塔燈網(wǎng)絡(luò),免費(fèi)獲得網(wǎng)站建設(shè)方案及報價

咨詢相關(guān)問題或預(yù)約面談,可以通過以下方式與我們聯(lián)系

業(yè)務(wù)熱線:余經(jīng)理:13699882642

Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.