版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
Flutterinaction第一章Flutter開源工具1AOPforFlutter開發(fā)利器:AspectD“碼”上用FlutterBoost開始混合第二章閑魚:Flutter企業(yè)級應(yīng)用實踐42Flutter&FaaS云端一體化架構(gòu)42第三章混合開發(fā)實踐指南59FlutterPlugin調(diào)用NativeAPIsFlutter混合工程改造實踐閑魚Flutter混合工程持續(xù)集成的最佳實踐第四章Flutter深入進階教程115一章節(jié)教會你如何低成本實現(xiàn)Flutter富文本揭秘!一個高準確率的Flutter埋點框架如何設(shè)計萬萬沒想到Flutter這樣外接紋理可定制化的Flutter相冊組件竟如此簡單流言終結(jié)者-Flutter和RN誰才是更好的跨端開發(fā)方案?閑魚Flutter應(yīng)用框架:FishRedux3月5日,閑魚宣布在GitHub上開源FishRedux,F(xiàn)ishRedux是一個基于Redux數(shù)據(jù)管理的組裝式flutter應(yīng)用框架,特別適用于構(gòu)建中大型的復(fù)雜應(yīng)用,它性能表現(xiàn)。下文中,我們將詳細介紹FishRedux的特點和使用過程,以下內(nèi)容來自InfoQ獨家對閑魚Flutter團隊的采訪和Fi在閑魚接入Flutter之初,由于我們的落地的方案希望是從最復(fù)雜的幾個主鏈路頁面需要集中狀態(tài)管理,也就是說頁面的不同組件共享一個數(shù)據(jù)來源,數(shù)據(jù)來頁面的UI展現(xiàn)形式比較多(如普通詳情、閑魚幣詳情、社區(qū)詳情、拍賣詳情等工作量大,所以UI組件需要盡可能復(fù)用,也就是說需要比較好的進行組現(xiàn),沒有任何一個框架可以既解決集中狀態(tài)管理,又能解決UI的組件化的,因為本組件化,當(dāng)然問題也非常明顯,針對復(fù)雜的詳情和發(fā)布業(yè)務(wù),往往業(yè)務(wù)邏輯很多,無第二個版本針對第一個版本的問題,做出了比較重大的修改,解決了UI代碼和邏輯代碼的分治問題,但同時,按照redux的標準,打破了redux的原則,對于精因此,在第三個版本進行重構(gòu)時,我們確立了整體的架構(gòu)原則與分層要求,一方面按照reduxjs的代碼進行了flutter側(cè)的redux實現(xiàn),將redux的原則完整保留下至此,我們完成了fishreduxFishRedux技術(shù)解析架構(gòu)圖:主體自底而上,分兩層,每一層用來解決不通層面的問題和矛盾,下面Redux所有對數(shù)據(jù)的增刪改查等操作都由Redux來集中負責(zé)。Redux是一個函數(shù)式的數(shù)據(jù)管理的框架。傳統(tǒng)OOP做數(shù)據(jù)管理,往往是定義一些Bean,每一個Bean對外暴露一些Public-API用來操作內(nèi)部數(shù)據(jù)(充函數(shù)式的做法是更上一個抽象的緯度,對數(shù)據(jù)的定義是一些Stru而操作數(shù)據(jù)的方法都統(tǒng)一到具有相同函數(shù)簽名(T,Action)=>T的R/wiki/Plain_old_Java_objectRedux的缺點Redux核心僅僅關(guān)心數(shù)據(jù)管理,不關(guān)心具體什么場景來使用它,這是它的優(yōu)點Redux的Reducer需要一層層手動組裝,帶來的繁瑣性和易錯性。FishRedux的改良FishRedux通過Redux做集中化的可觀察的數(shù)據(jù)管理。然不僅于此,對于傳一個組件需要定義一個數(shù)據(jù)(Struct)和一個R依賴子的關(guān)系。通過這層依賴關(guān)系,我們解決了【集中】和【分治】之間的矛盾,同時對Reducer的手動層層Combine變成由框架自動完成,大大簡化了使用Redux對社區(qū)標準的followState、Action、Reducer、Store、Middleware以上概念和社區(qū)的ReduxJS是完全一致的。我們將原汁原味地保留所有的Redux的優(yōu)勢。Component組件是對局部的展示和功能的封裝?;赗edux的原則,我們對功能細分為修改數(shù)據(jù)的功能(Reducer)和非修改數(shù)據(jù)的功能(副作用Effect)。于是我們得到了,View、Effect、Reducer三部分,稱之為組件的三要素,分這是一種面向當(dāng)下,也面向未來的拆分。在面向當(dāng)下的Redux看來,是數(shù)據(jù)管理和其他。在面向未來的UI-AutomUI的表達對程序員而言即將進入黑盒時代,研發(fā)工程師們會把更多的精力放在組件是對視圖的分治,也是對數(shù)據(jù)的分治。通過逐層分治,我們將復(fù)雜的頁面和關(guān)于ViewView僅僅是一個函數(shù)簽名:(T,Dispatch,ViewService)=>Widget它主要包含需要用到的組件依賴等,通過ViewService標準化調(diào)用。比如一個典型的符關(guān)于EffectEffect是對非修改數(shù)據(jù)行為的標準定義,它是一個函數(shù)簽名:(Context,Action)接收來自View的“意圖”,也包括對應(yīng)的生命周期的回調(diào),然后做出具體的它的處理可能是一個異步函數(shù),數(shù)據(jù)可能在過程中被修改,所以我們不崇尚持關(guān)于ReducerReducer是一個完全符合Redux規(guī)范的函數(shù)簽名:(T,Action)=>T一些符合簽名的Reducer:同時我們以顯式配置的方式來完成大組件所依賴的小組件、適配器的注冊,這份依賴配置稱之為Dependencies。Dependencies(可選)。通過Component的抽象,我們得到了完整的分治,多緯度的復(fù)用,更好的AdapterAdapter也是對局部的展示和功能的封裝。它為ListView高性能場景1)將一個”Big-Cell”放在Component里,無法享受ListView代碼的性能2)Component無法區(qū)分appear|disappear和i概括的講,我們想要一個邏輯上的ScrollView,性能上的ListView,這樣的一種局部展示和功能封裝的抽象。做出這樣獨立一層的抽象是我們看實際的效果,我們Reducerislong-lived,Effectismedium-lived,Viewisshort-lived.使用框架前我們的詳情頁面的FPS,基線在52FPS;使用框架,僅使用Component抽象下,F(xiàn)PS下降到40,遭遇“Big-Cell”使用框架,同時使用Adapter抽象后,F(xiàn)PS提升到53,回到基線以上,有小DirectoryCommunicationMechanism簡單的描述:采用的是帶有一段優(yōu)先處理的廣播,self-first-broadcast。發(fā)出的Action,自己優(yōu)先處理,否則廣播給其他組件和Redu通過一個簡單而直觀的dispatch完成了組件內(nèi),組件間(父到子,子到父,兄弟間RefreshMechanism局部數(shù)據(jù)修改,自動層層觸發(fā)上層數(shù)據(jù)的淺拷貝,對一方面是對Redux數(shù)據(jù)修改的嚴格的foFishRedux的優(yōu)點通過Redux做集中化的可觀察的數(shù)據(jù)管理。我們將原汁原味地保留所有的Redux的優(yōu)勢,同時在Reducer的合并上,變成由框架代理自動完成,大大簡化了組件既是對視圖的分治,也是對數(shù)據(jù)的分治。通過逐層分治,我們將復(fù)雜的頁面View、Reducer、Effect隔離將組件拆分成三個無狀態(tài)的互不依賴的函數(shù)。因為是無狀態(tài)的函數(shù),它更易于編組件、適配器通過自由的聲明式配置組裝來完成。包括它的View、Reducer、核心框架保持自己的核心的三層關(guān)注點,不做核心關(guān)注點以外的事情,同時對上框架和其他中間件的打通,諸如自動曝光、高可用等,各中間件和框架之間都開源之后,閑魚打算通過以下方式來維護FishRedux:通過后續(xù)的一系列的對外宣傳,吸引更多的開發(fā)者加入或者使用。目前Flutter進一步提供,一系列的配套的開發(fā)輔助調(diào)試工具,提升上層Flutter開發(fā)效率FishRedux目前已在阿里巴巴閑魚技術(shù)團隊內(nèi)多場景,深入應(yīng)用。最后Talkischeap,Showmethecode,我們今天正式在GitHub上開源,更多內(nèi)容,請到AOPforFlutter開發(fā)利器:AspectD隨著Flutter這一框架的快速發(fā)展,有越來越多的業(yè)務(wù)開始使用Flutter來重構(gòu)跨平臺表現(xiàn)好,另一方面Flutter也面臨著插件,基礎(chǔ)能力,底層框架缺失或者不完舉個栗子,我們在實現(xiàn)一個自動化錄制回放的過程中發(fā)現(xiàn),需要去修改Flutter框架(Dart層面)的代碼才能夠滿足要求,這就會有了對框架的侵入性。要解決這種侵入性的問題,更好地減少迭代過程中的維護成本,我們考慮的首要方案即面向切面那么如何解決AOPforFlutter這個問題呢?本文將重點介紹一個閑魚技術(shù)團隊開發(fā)的針對Dart的AOP編程框架AspectD。AspectD:面向Dart的AOP框架AOP能力究竟是運行時還是編譯時支持依賴于語言本身的特點。舉例來說在iOS中,ObjectiveC本身提供了強大的運行時和動態(tài)性使得運行期AOP簡單易用。在Android下,Java語言的特點不僅可以實現(xiàn)類似AspectJ這樣的基于字節(jié)碼修改的編譯期靜態(tài)代理,也可以實現(xiàn)SpringAOP這樣的基于運行時增強的運行期動態(tài)代理。那么Dart呢?一來Dart的反射支持很弱,只支持了檢查(Introspection),不因此,我們設(shè)計實現(xiàn)了基于編譯期修改的AOP方案AspectD。AOP設(shè)計詳圖典型的AOP場景}}面向開發(fā)者的API設(shè)計PointCut需要完備表征以怎么樣的方式(Call/Execute等),向哪個Library,}}其中包含了源代碼信息(如庫名,文件名,行號等),方法調(diào)用對象,函數(shù)名,參數(shù)信息等。請注意這里的@pragma('vm:entry-point')注解,其核心邏輯在于Tree-Shaking。在AOT(aheadoftime)編譯下,如果不能被應(yīng)用主入口(main)最終可能調(diào)到,那么將被視為無用代碼而丟棄。AOP代碼因為其注入邏輯的無侵入性,顯然是不會被main調(diào)到的,因此需要此注解告訴編譯器不要丟棄這段邏輯。此處的proceed方法,類似AspectJ中的ProceedingJoinPceed()方法,調(diào)用ceed()方法即可實現(xiàn)對原始邏輯的調(diào)用。原始定義中的}此處的@pragma("vm:entry-point")效果同a中所述,point參數(shù)傳入AOP方法,使開發(fā)者可以獲得源代碼調(diào)用信息的相關(guān)信息,實現(xiàn)自身邏輯}Aspect的注解可以使得ExecuteDemo這樣的AOP實現(xiàn)類被方便地識別和提取,也可以起到開關(guān)的作用,即如果希望禁掉此段AOP邏輯,移除@Aspect注解AOP代碼的編譯在AOT編譯(Release模式下),Tree-Shaking邏輯使得當(dāng)aop_impl.dart中的內(nèi)容沒有被aop中main調(diào)用時,其內(nèi)容將不會編譯到dill中。通過添加@當(dāng)我們用AspectD寫出AOP代碼,透過編譯aop.dart生成中間產(chǎn)物,使得dill中既包含了原始項目代碼,也包含了AOP代碼后,則需要考慮如何對其修改。在AspectJ中,修改是通過對Class文件進行操作實現(xiàn)的,在AspectD中,我們則dart提供了一種KerneltoKernelTransform的方式,可以通過對dill文件的基于開發(fā)者編寫的AspectD注解,AspectD的變換部分可以提取出是哪些庫/類/方法需要添加怎樣的AOP代碼,再在AST遞歸的過程中通過對目標類的操作,實現(xiàn)Call/Execute這樣的功能。}}}}}合開發(fā)者書寫的AspectD注解(此處的_aspectdInfoMap_和aspectdItemInfo),可以對原始的AST對象(此處methodInvocation)進行變換,從而改變原始的代碼AspectD支持的語法}}Execute}Inject僅支持Call和Execute,對于Flutte禁止了反射,退一步講,即便Flutter開啟了反射支持,依然很弱,并不能滿足需求。舉個典型的場景,如果需要注入的dart代碼里,x.dart文件的類y定義了一個私有方法m或者成員變量p,那么在aop_impl.dart中是沒有辦法對其訪問的,更不用說多個連續(xù)的私有變量屬性獲得。另一方面,僅僅對方法整體進行操作可能是不夠的,我們可能需要在方法的中間插入處理邏輯。為了解決這一問題,AspectD設(shè)}如果我們想要在onTapCancel之后添加一段對于instance和context的處理邏輯,Call和Execute是不可行}}通過上述的處理邏輯,經(jīng)過編譯構(gòu)建后的dill中的GestureDetector.build方法此外,Inject的輸入?yún)?shù)相對于Call/Execute而言,多了一個lineNum的命名雖然我們可以通過編譯aop.dart達到同時編譯原始工程代碼和AspectD代碼到dill文件,再通過Transform實現(xiàn)dill層次的變換實現(xiàn)AOP,但標準的flutter構(gòu)建(即flutter_tools)并不支持這個過程,所以還是需要對構(gòu)建過程做細微修改。在AspectJ中,這一過程是由非標準Java編譯器的Ajc來實現(xiàn)的。在AspectD--3way/Users/kylewong/Codes/AOP/aspectd/0001-aspectd.patchkylewong@KyleWongdeMacBook-Profluttermaster%rmbin/cache/flutter_tools.stampkylewong@KyleWongdeMacBook-Profluttermas-基于AspectD,我們在實踐中成功地移除了所有對于Flutter框架的侵入性代碼,實現(xiàn)了同有侵入性代碼同樣的功能,支撐上百個腳本的錄制回放與自動化回歸穩(wěn)從AspectD的角度看,Call/Execute可以幫助我們便捷實現(xiàn)諸如性能埋點(關(guān)鍵方法的調(diào)用時長),日志增強(獲取某個方法具體是在什么地方被調(diào)用到的詳細信息),Doom錄制回放(如隨機數(shù)序列的生成記錄與回放)等功能。Inject語法則更為強大,可以通過類似源代碼諸如的方式,實現(xiàn)邏輯的自由注入,可以進一步來說,AspectD的原理基于Dill變換,有了Dill操作這一利器,開發(fā)者可以自由地對Dart編譯產(chǎn)物進行操作,而且這種變換面向的是近乎源代碼級別的AST對象,不僅強大而且可靠。無論是做一些邏輯替換,還是是Json<-->模型轉(zhuǎn)AspectD作為閑魚技術(shù)團隊新開發(fā)的面向Flutter的AOP框架,已經(jīng)可以支持主流的AOP場景并在Github開源,歡迎使用。AspectdforFlutter如果你在使用過程中,有任何問題或者建議,歡迎提issue或者PR.“碼”上用FlutterBoost開始混合開發(fā)吧具有一定規(guī)模的App通常有一套成熟通用的基礎(chǔ)庫,尤其是阿里系A(chǔ)pp,一般用的穩(wěn)健型方式。閑魚在實踐中沉淀出一套自己的混合技術(shù)方案。在此過程中,我們跟GoogleFlutter團隊進行著密切的溝通,聽取了官方的一些建議,同時也針對我Flutter技術(shù)鏈主要由C++實現(xiàn)的FlutterEngine和Dart實現(xiàn)的Framework一個進程里面最多只會初始化一個DartVM。然而一個進程可以有多個FlutterEngine,多個Engine實例共享同一個Da我們來看具體實現(xiàn),在iOS上面每初始化一個FlutterViewController就會有一個引擎隨之初始化,也就意味著會有新的線程(理論上線程可以復(fù)用)去跑Dart代碼。Android類似的Activity也會有類似的效果。如果你啟動多個引擎實例,注意此時DartVM依然是共享的,只是不同Engine實例加載的代碼跑在各自獨立的在混合方案方面,我們跟Google討論了可能的一些方案。Flutter官方給出的建議是從長期來看,我們應(yīng)該支持在同一個引擎支持多窗口繪制的能力,至少在邏輯上做到FlutterViewController是共享同一個引擎的資源的。換句話說,我們希望所我們在混合方案中解決的主要問題是如何去處理交替出現(xiàn)的Flutter和Native頁面。Google工程師給出了一個KeepItSimple的方案:對于連續(xù)的Flutter頁面(Widget)只需要在當(dāng)前FlutterViewC我們初始化新的引擎。例如,我們進行下面一組導(dǎo)航操作:FlutterPage1->FlutterPage2->NativePage1->FlutterPage3我們只需要在FlutterPage1和FlutterPage3創(chuàng)建不同的這個方案的好處就是簡單易懂,邏輯清晰,但是也有潛在的問題。如果一個Native頁面一個Flutter頁面一直交替進行的話,F(xiàn)lutterEngine的數(shù)量會線性增冗余的資源問題.多引擎模式下每個引擎之間的Isolate是相互獨立的。在邏輯上這并沒有什么壞處,但是引擎底層其實是維護了圖片緩存等比較消耗內(nèi)存插件注冊的問題。插件依賴Messenger去傳遞消息,而目前Messenger是由FlutterViewController(Activity)去實現(xiàn)的。如果你有多個FlutterView-Native的頁面是VC。邏輯上來說我們增加頁面之間通信的復(fù)雜度。如果所有Dart代碼都運行在同一個引擎實例,它們共享一個Isolate,可以用統(tǒng)一的編程框架進行Widget之間的通信,多前面我們提到多引擎存在一些實際問題,所以閑魚目前采用的混合方案是共享同一個引擎的方案。這個方案基于這樣一個事實:任何時候我們最多只能看到一個頁面,當(dāng)然有些特定的場景你可以看到多個ViewController,但是這些特殊場景我們我們可以這樣簡單去理解這個方案:我們把共享的FlutterView當(dāng)成一個畫布,然后用一個Native的容器作為邏輯的頁面。每次在打開一個容器的時候我們通過通信機制通知FlutterView繪制成當(dāng)前的邏輯頁面,然后將FlutterView放到當(dāng)前容老方案在Dart側(cè)維護了一個Navigator棧的結(jié)構(gòu)。棧數(shù)據(jù)結(jié)構(gòu)特點就是每次只能從棧頂去操作頁面,每一次在查找邏輯頁面的時候如果發(fā)現(xiàn)頁面不在棧頂那么需要往回Pop。這樣中途Pop掉的頁面狀態(tài)就丟失了。這個方案無法支持同時存在多個平級邏輯頁面的情況,因為你在頁面切換的時候必須從棧頂去操作,無法再保持狀態(tài)舉個例子:有兩個頁面A,B,當(dāng)前B在棧頂。切換到A需要把B從棧頂Pop出去,此時B的狀態(tài)丟失,如果想切回B,我們只能重新打開B之前頁面的狀態(tài)無如在pop的過程當(dāng)中,可能會把Flutter官方的Dialog進行誤殺而且基于棧的操作我們依賴對Flutter框架的一個屬性修改,這讓這個方案具有第二代混合技術(shù)方案FlutterBoost在閑魚推進Flutter化過程當(dāng)中,更加復(fù)雜的頁面場景逐漸暴露了老方案的局限性和一些問題。所以我們啟動了代號FlutterBoost(向C++Boost致敬)的新混合可復(fù)用通用型混合方案支持更加復(fù)雜的混合模式。比如支持主頁Tab這種情況支持通用頁面生命周期統(tǒng)一明確的設(shè)計概念跟老方案類似,新的方案還是采用共享引擎的模式實現(xiàn)。主要思路是由Native然后由容器去管理頁面的繪制。在Native側(cè)我們只需要關(guān)心如果初始化容器,然后ContainerManager:容器Messaging:基于Channel的消息通信Messaging:基于Channel的消息通信在Native和Flutter表示頁面的對象和概念是不一致的。在Native,我們對于頁面的概念一般是ViewController,Activity。而對于Flutter我們對于頁面的概念是Widget。我們希望可統(tǒng)一頁面的概念,對應(yīng)的頁面概念。換句話說,當(dāng)一個Native的頁面容器存在的時候,F(xiàn)lutteBoost保證一定會有一個Widget作為容器的內(nèi)容。所以我們在理解和進行路由操作的時候那么在FlutterBoost的概念里說到頁面的時候,我們指的是Native容器和它所容器的直接操作。無論路由請求來自何方,最終都會另一方面,我們無法控制業(yè)務(wù)代碼通過Flutter本身的Navigator去push新的Widget。對于業(yè)務(wù)不通過FlutterBoost而直接使用Navigator操作Widget的情前面我們提到老方案在Dart層維護單個Navigator棧結(jié)構(gòu)用于Widget的切換。而新的方案則是在Dart側(cè)引入了Container的概念,不再用棧的結(jié)構(gòu)去維護現(xiàn)有的頁面,而是通過扁平化key-value映射的形式去維護當(dāng)前所有的頁面,每個頁面擁有一個唯一的id。這種結(jié)構(gòu)很自然的支持了頁面的查找和切換,不再受制于棧頂操作的問題,之前的一些由于pop導(dǎo)致的問題迎刃而解。同時也不再需要依賴修改Flutter在底層提供了讓你自定義Navigator的接口,我們自己實現(xiàn)了一個管理多個Navigator的對象。當(dāng)前最多只會有一個可見的FlutterNavigator,這個3.FlutterContainerManager進而得到通知,負責(zé)創(chuàng)建出對應(yīng)的Flutter容4.當(dāng)Native容器展示到屏幕上時,容器發(fā)消息給Flut5.FlutterContainerManager找到對應(yīng)id的Flutt這就是一個新頁面創(chuàng)建的主要邏輯,銷毀和進入后臺等操作也類似有Native容目前FlutterBoost已經(jīng)在生產(chǎn)環(huán)境支撐著在閑魚我們在項目啟動之初就希望FlutterBoost能夠解決NativeApp混合模式接入Flutter這個通用問題。所以我們把它做成了一個可復(fù)用的Flutter插件,希望吸引更多感興趣的朋友參與到Flutter社區(qū)的建設(shè)。我們的方案可能不是最好的,這個方案距離完美還有很大的距離,我們希望通過多分享交流以推動Flutter技術(shù)社區(qū)的發(fā)展在有限篇幅中,我們分享了閑魚在Flutter混合技術(shù)方案中積累的經(jīng)驗和代碼。在兩個Flutter頁面進行切換的時候,因為我們只有一個FlutterView所以需要對上一個頁面進行截圖保存,如果Flutter頁面多截圖會占用大量內(nèi)存。這里我們采用文件內(nèi)存二級緩存策略,在內(nèi)存中最多只保存2-3個截圖,其余的寫入文件按需頁面渲染性能方面,F(xiàn)lutter的AOT優(yōu)勢展露無遺。在頁面快速切換的時候,F(xiàn)lutter能夠很靈敏的相應(yīng)頁面的切換,在邏輯上創(chuàng)造出一種Flutter多個頁面的Release1.0支持項目開始的時候我們基于閑魚目前使用的Flutter版本進行開發(fā),而后進行了Release1.0兼容升級測試目前沒有發(fā)現(xiàn)問題。只要是集成了Flutter的項目都可以用官方依賴的方式非常方便的以插件形式引入FlutterBoost,只需要對工程進行少量代碼接入即可完成接入。詳細接入文檔,目前,第二代混合棧已全面閑魚全面應(yīng)用。我們非常樂意將沉淀的技術(shù)回饋給社區(qū)。歡迎大家一起貢獻,一起交流,攜手共建Flutteflutter-boot,一分鐘搞定混合工程搭建!Flutter,從誕生起到現(xiàn)在,已經(jīng)成為了跨端開發(fā)的領(lǐng)跑者司走上了flutter探索之路。Flutter的主要開發(fā)模式分成兩種,一種是獨立app的模原生工程可以在任何的目錄結(jié)構(gòu)下,和flutter工程地址不產(chǎn)生關(guān)聯(lián),但需要在原生工程結(jié)構(gòu)中聲明flutter工程的本地地址。閑魚應(yīng)用在flutter能夠以模塊形式存在前,進行了很長時間的混合app架構(gòu)的進行了大量調(diào)研,最終推出了一套開箱即用的混合工程腳手架flutter-boot,幫助大flutter-boot核心解決了混合開發(fā)模式下的兩個問題:flutter混合開發(fā)的工程化首先在工程化設(shè)計的問題上,flutter-boot建立了一套標準的工程創(chuàng)建流程和友好的交互命令,當(dāng)流程執(zhí)行完成后,即擁有了混合開發(fā)的標準工程結(jié)構(gòu),這一套工程另外在混合棧的問題上,flutter-boot能自動注入混合棧依賴,同時將核心的混合棧接入代碼封裝后注入到原生工程內(nèi),在用戶按提示插入簡單幾行模版代碼后,即使用flutter-boot搭建的混合工程,開箱即可使用,接下來讓我們了解下了解官方的AddFluttertoexistingapps項目AddFluttertoexistingapps項目會引導(dǎo)我們以module的形式創(chuàng)建f在官方的工程結(jié)構(gòu)下,.ios和.androi工程目錄下運行時即通過這兩個工程來啟動應(yīng)用。那我們?nèi)绾巫屧こ毯彤a(chǎn)生關(guān)聯(lián)低否低是低是高是以直接以源碼的方式集成,flutterplugindart只有在被業(yè)務(wù)代碼引用時才有效,因此和業(yè)務(wù)代碼一樣,需要支持dart代碼的調(diào)試模式和發(fā)布模式,因此dart代碼的關(guān)聯(lián)會侵入到app的構(gòu)建環(huán)節(jié),根據(jù)app構(gòu)建的模式來決定dart代碼的構(gòu)建模式。具體的實現(xiàn),拿iOS來舉例,我們會在podfile文件中增加一個自定義的ruby腳flutterpluginnative的源碼引用,同時聲明業(yè)務(wù)代碼的路徑。接下來會介入構(gòu)建流程,在xcode的buildphase內(nèi)加入shell腳本xcode_backend的調(diào)用,xcode_flutter-boot的補充創(chuàng)建和git倉庫的部署,fluttermodule創(chuàng)建命令調(diào)用前,我們會做基礎(chǔ)的檢查來讓略部分文件,同時我們會對倉庫的狀態(tài)進行檢查,在倉庫為空時,直接添加文件,在生工程我們將原生工程進行了軟鏈接,鏈接到flutter工程的ios目錄和android下運行iOS工程會存在一個限制,即iOS工程的target需要指定為runner,為了解決這個問題,我們將原生工程的主target進行了復(fù)制,復(fù)制了一份名為runner的同時,為了支持遠程構(gòu)建的模式,我們flutter倉庫本地路徑的聲明根據(jù)構(gòu)建模式進行了區(qū)分,封裝在自定義的依賴腳本中,例如在iOS工程內(nèi),我們會添加local.json中。remotelink過程目的在于遠端構(gòu)建模式下,能夠獲取flutter倉庫的代碼,并在遠端機器上進行構(gòu)建。在遠端構(gòu)建模式下,我們會侵入依賴管理的過程,在依賴獲取錄聲明為flutter倉庫本地路徑,拉取flutter代碼并進行本地部署的過程,我們稱之那遠端模式和本地模式如何區(qū)分呢?為了區(qū)分遠端模式與本地模式,我們將遠端的flutter倉庫信息記錄在fbConfig.json,同時json文件,這樣只需要初始化混合工程的工程師運行一次remotelink,其他的開發(fā)為了方便快速搭建,我們提供了一個命令集合,命名為init,我們將必備的環(huán)節(jié)混合棧是閑魚開源的一套用于flutter混合工程下協(xié)調(diào)原生頁面與flutter頁面交互的框架,目前是混合開發(fā)模式下的主流框架。在混合棧開源后,我們關(guān)注到大量開發(fā)者在集成混合棧時會產(chǎn)生各種環(huán)境配置或代碼添加導(dǎo)致的集成問題。因此我們決定要進行適配,即我們集成的混合棧版本也需要變更。因此我們將混合棧的版本配置通在調(diào)研了混合棧的使用過程后,我們將混合棧需要的demo代碼分成了四個2.頁面路由的配置4.原生的測試跳轉(zhuǎn)入口引擎的托管我們依賴于應(yīng)用的初始化,由于初始化過程隨著應(yīng)用的復(fù)雜程度提升而提升,因此目前我們提供了一行代碼作為接口,使用者在應(yīng)用初始化時加入這一行路由配置即路由到某個標識符時,flutter或原生頁面需要識別并跳轉(zhuǎn)相應(yīng)頁面。路由的配置需要在原生和flutter兩側(cè)進行部署。在原生側(cè),我們將混合棧的demo路由代碼進行了精簡,然后添加在了原生工程的固定目錄下。由于iOS僅添加代碼文件是不會被納入構(gòu)建范圍的,因此我們封裝了一套iOS側(cè)的代碼添加工具來實現(xiàn)文為了方便使用者快速看到混合工程的跳轉(zhuǎn)模式,我們在iOS和android雙端封裝了一個入口按鈕和按鈕的添加過程,使用者在測試的頁面手動加入一行代碼,即可在使用flutter-boot前,開發(fā)者可用者只需要調(diào)用一個命令,加入兩行代碼即可完成混合工程的搭建,大大降低了開發(fā)flutter-boot的使命還未達成,我們期望使用者能更加流暢的進行flutter開發(fā),未來我們會優(yōu)化多人協(xié)同的開發(fā)流程,完善持續(xù)集成環(huán)境的搭建,讓使用者擁有更佳Flutter&FaaS云端一體化架構(gòu)就近一年來閑魚在Flutter&FaaS一體化項目上的探索和實踐進行了分享。傳統(tǒng)Native+Web+服務(wù)端混合開發(fā)的挑戰(zhàn)隨著無線,IoT的發(fā)展,5G的到來,移動研發(fā)越發(fā)向多端化發(fā)展。傳統(tǒng)的基于Native+Web+服務(wù)端的開發(fā)方式,研發(fā)效率低下,顯然已經(jīng)無法適應(yīng)發(fā)展我們希望探索閑魚這樣規(guī)模的獨立APP的高效研發(fā)架構(gòu)。主要思路是圍繞Flutter解決多端問題,并使Flutter與FaaS等無服務(wù)容能力打通,形成云端一體化的研發(fā)能力,支持一云多端的發(fā)展需要。在某些場景已經(jīng)取得效果,希望分享過程中跨端方案Flutter與RN的對比和選擇沒有銀彈的解決方案,F(xiàn)lutter與RN各有優(yōu)點。如何選擇因素很多,關(guān)鍵看如當(dāng)前團隊人員以前端JS棧為主還是Native為主?如果JS為主,寫RN會更習(xí)慣。如果Android或iOS動態(tài)性和復(fù)雜交互的性能,哪個更重要?動態(tài)性重要RN合適,性能體驗重要Flutter不會失望。雖然Flutter也有一些動態(tài)化解決方案,例如JS轉(zhuǎn)接Flutter引擎的方案,Dart代碼CodePush的方案,組件化服務(wù)端組裝方案等,但這些動態(tài)方案都沒有RN這樣從JS層解決的這么好。Dart作為FaaS層的第一可選語言云端技術(shù)棧的打通,是減少協(xié)同的不錯的解法。以往前端+強類型,可預(yù)測性GC異步和并發(fā)高性能的JITProfiler閑魚首先嘗試將Dart作為普通的Server,替代傳統(tǒng)的JavaServer,然后再將Dart容器嵌入到FaaS容器中。建立DartServer能力是第一步,也是主要的工受Flutter的HotReload啟發(fā),將HotReload移植到了利用Isolate,在開發(fā)環(huán)境中為每個開發(fā)人員分配一個Isolate,解決以往的環(huán)Dart本身是單線程異步模型利用Dart的Zone的特性,可以方便的實現(xiàn)調(diào)用鏈路的跟蹤,方便記錄Trace日志。利用Dart支持的C++Extension能力,可以在Dart中訪問支持了C++的中間件包。另外,ServerMesh也是一個重要的思路,用于解耦異構(gòu)語言之上述內(nèi)容實現(xiàn)了Flutter&DartFaaS的技術(shù)棧的統(tǒng)一,但僅技術(shù)棧統(tǒng)一還遠遠不夠,端、云的同學(xué)仍然無法真正互補和一體化打通,原因在于還有更多深入問題需一體化的業(yè)務(wù)閉環(huán)紅利如何最大化?一體化不僅是效率的提升,還使一個同學(xué)如何消除云端技術(shù)壁壘?僅技術(shù)棧打通,端人員還是不會寫云,原因在于對云業(yè)務(wù)閉環(huán)為業(yè)務(wù)開發(fā)同學(xué)帶來更好的成長空間,可以完整和專注的思考業(yè)務(wù)。業(yè)務(wù)閉環(huán)是業(yè)務(wù)流程沉淀的方向以往的架構(gòu)是云、端分開架構(gòu)的,一體化后有了更多的架構(gòu)下沉空間,從而帶領(lǐng)域下沉和工具支撐是一體化的保證案例一,一體化在資源均衡方面的體現(xiàn)。在近期的一個項目中,云端一體化使原案例二,一體化在業(yè)務(wù)閉環(huán)方面的體現(xiàn)。負責(zé)增長的一位開發(fā)同學(xué),專注在增長業(yè)務(wù)上,在合適的情況下為合適的人投放合適的內(nèi)容,以此帶來用戶的增長和活躍效一體化是建設(shè)高效研發(fā)框架的方向,并不是所有場景都需要一體化的開發(fā),但一體化的Flutter、FaaS等技術(shù)組件,可以獨立使用,也會帶來效率提升,并且與原有的開發(fā)模式兼容。從一體化的思路去建設(shè),可以使整體架構(gòu)體系更加一致,也有機會做一體的架構(gòu)沉淀。未來閑魚希望在一體化上做更多嘗試和深入探索,包基于Flutter的架構(gòu)演進與創(chuàng)新2012年應(yīng)屆畢業(yè)加入阿里巴巴,主導(dǎo)了閑魚基于Flutter的新混合架構(gòu),同時Flutter的優(yōu)勢與挑戰(zhàn)Flutter是Google開源的跨端便攜UI工具包,除了具有非常優(yōu)秀的跨端渲通過以上的特點可以看出,F(xiàn)lutter可以極大的加速客戶端的研發(fā)效率,與此同中小型的客戶端團隊非常適合Flutter開發(fā),不僅一端編寫雙端產(chǎn)出,還有效App在Android市場占比遠高于iOS的團隊,比如出海東南亞的一些App,以量產(chǎn)App為主要策略的團隊,不論是量產(chǎn)ToB的企業(yè)App,還是有針對性的產(chǎn)出不同領(lǐng)域的ToC的App的公司,都可以通過一端開發(fā)多端產(chǎn)出的閑魚在以上的場景中屬于第一種場景,服務(wù)3億用戶的閑魚App的背后,是十幾名客戶端開發(fā),與競對相比,我們是一只再小不過的團隊,在這種場景下,F(xiàn)l但與此同時,F(xiàn)lutter在設(shè)計上帶來的優(yōu)勢同時又會帶來新的問題。所有的新技術(shù)都是脫胎于老技術(shù)的,F(xiàn)lutter也不例外,其身上帶有很多Chrome的影子。我們?nèi)绻谝粋€已經(jīng)存在的App中加入Flutter,如何讓Native與Flutter進行無如果在Flutter的容器中,使用已有的NativeUI組件,在Flutter與Native已有App+Flutter容器在沒有任何改造的情況下以iOS為例,你可以通過創(chuàng)建新的FlutterViewCon-troller來創(chuàng)建一個新的Flutter容器,這個方案下,當(dāng)創(chuàng)建多個FlutterViewCon-troller時會同時在內(nèi)存中創(chuàng)建多個FlutterEngine的Runtime(雖然底層D這種情況下,閑魚選擇了全局共享同一個FlutterViewController的方式保證了內(nèi)存占用的最小化,同時通過基礎(chǔ)框架FlutterBoost提供了Native棧與Flutter棧的通信與管理,保證了當(dāng)Native打開或關(guān)閉一個新的Flutter頁面時,Dart側(cè)的Navigator也做到自動的打開或關(guān)閉一個新的Widget。目前Google官方的提供的然而在這種情況下,如果出現(xiàn)如閑魚圖中所示多個Tab的場景下,整個堆棧邏輯就會產(chǎn)生混亂,因此閑魚在這個基礎(chǔ)上對FlutterBoost的方案進行了升級并開源,通過在Dart側(cè)提供一個BoostContainerManager的方式,提供了對多個了一個類似WebView的OpenWindow的版本,因此需要支持1.5的同學(xué)等稍等,我們會在近期更新支持1.5的FlutterFlutter頁面+NativeUI由于閑魚是一個閑置交易社區(qū),因此圖片和視頻相對較多,對圖片視頻的線上性能以及內(nèi)存占用有較嚴格的要求。目前Flutter已提供的幾種方案中(PlatformView以及FlutterPlugin不論是對內(nèi)存的占用還是整個的線上流暢度上還存在一Plugin中提供的FlutterTextureRegistry的能力,在去年上半年我們優(yōu)先針對視頻的場景進行了優(yōu)化,優(yōu)化的思路主要是針對Flutte修改,將原有接口中必須傳入一個PixelBuffer的內(nèi)存對象這一限制做了擴展,增加一個新的接口保證其可以傳入一個GPU對象的TextureID。經(jīng)生成好的TextureID進行Flutter側(cè)的渲染,這樣就將鏈路從Native側(cè)生成的TextureID->copy的內(nèi)存對象PixelBuffer->生成新的TextureID->渲染,轉(zhuǎn)變?yōu)镹ative側(cè)生成的TextureID->渲染。整個鏈路極大的縮短,保證了整個的渲染效率以及更小的內(nèi)存消耗。閑魚在將這套方案上線后,又嘗試將該方案應(yīng)用于圖片渲染的場景下,使得圖片的緩存,CDN優(yōu)化,圖片裁切等方案與N集團中間件的性能優(yōu)化的同時,也得到了更小的內(nèi)存消耗,方案落地后,內(nèi)存溢出大目前該方案由于需要配合FlutterEngine的修改,因此暫時無法提供完整的方案至開源社區(qū),我們正在跟google積極溝通整個修改方案,相信在這一兩個月內(nèi)會將試驗性的EnginePatch開源至社區(qū),供有興趣的同學(xué)參考。將以上兩個問題解決以后,閑魚開始了Flutter在業(yè)務(wù)側(cè)的全面落地,然而很快如何提供一些標準供大家進行參考保證代碼的一致性如何將復(fù)雜業(yè)務(wù)進行有效的拆解變成子問題如何保證更多的同學(xué)可以快速上手并寫出性能和穩(wěn)定性都不錯的代碼在方案的前期,我們使用了社區(qū)的FlutterRedux方案,由于最先落地的詳情,發(fā)布等頁面較為復(fù)雜,因此我們有針對性的對View進行了組件化的拆分,但由于業(yè)務(wù)的復(fù)雜性,很快這套方案就出現(xiàn)了問題,對于單個頁面來說,State的屬性以及Reducer的數(shù)量都非常多,當(dāng)產(chǎn)生新需求堆疊的時候,修改困難,容易產(chǎn)生線上針對以上的情況,我們進行了整個方案的第二個迭代,在原有Page的基礎(chǔ)上提供了Component的概念,使得每個Component具備完整的Redux元素,保證了UI,邏輯,數(shù)據(jù)的完整隔離,每個Component單元下代碼相對較少,易于維護和開發(fā),但隨之而來的問題是,當(dāng)頁面需要產(chǎn)生數(shù)據(jù)同步時,整個的復(fù)雜性飆升,在Page的維度上失去了統(tǒng)一狀態(tài)管理的優(yōu)勢。在這種情況下閑魚換個角度看端側(cè)的架構(gòu)設(shè)計,我們參考ReactRedux框架中的Connect的思想,移除掉在Component的Store,隨之而來的是新的Connec-tor作為Page和Component的數(shù)據(jù)聯(lián)通的橋梁,我們基于此實現(xiàn)了PageState到ComponentState的轉(zhuǎn)換,以及ComponentState變化后對PageState的自動同步,從而保證了將復(fù)雜業(yè)務(wù)有效的拆解成子問題,同時享受到統(tǒng)一狀態(tài)管理的優(yōu)勢。與此同時基于新的框架,在統(tǒng)一了大家的開發(fā)標準的情況下,新框架也在底層有針對性的提供了對長列表,多列表拼接等case下的一些性能優(yōu)化,保證了每一位同學(xué)在按照標準開發(fā)后,可以得到相對目前市面上其他的Flutter業(yè)務(wù)框架相比更好的目前這套方案FishRedux已經(jīng)在github開源,目前支持閑魚在去年經(jīng)歷了業(yè)務(wù)的快速成長,在這個階段上,我們同時進行了大量的Flutter的技術(shù)改造和升級,在嘗試新技術(shù)的同時,如何能保證線上的穩(wěn)定,線下的有更多的時間進行新技術(shù)的嘗試和落地,我們需要一些新的思路和工作方式上以我們?nèi)粘9ぷ鳛槔?,F(xiàn)lutter的研發(fā)同學(xué),在每次開發(fā)完成后,需要在本地進行Flutter產(chǎn)物的編譯并上傳到遠端Repo,以便對Native同學(xué)透明發(fā)不受Flutter改造的干擾。在這個過程中,F(xiàn)lutter側(cè)的業(yè)務(wù)開發(fā)同學(xué)面臨包上傳更新同步等繁瑣的工作,一不小心就會出錯,后續(xù)的排查等讓Flutter前期的開發(fā)變成了開發(fā)5分鐘,打包測試2小時。同時Flutter到底有沒有解決研發(fā)效率快的問題,以及同學(xué)們在落地過程中有沒有Follow業(yè)務(wù)架構(gòu)的標準,這一切都是在痛定思痛以后,我們認為數(shù)據(jù)化+自動化是解決這些問題的一個較好的思路。因此我們首先從源頭對代碼進行管控,通過commit,將代碼與后臺的需求以及bug一一關(guān)聯(lián),對于不符合要求的commit信息在完成代碼和任務(wù)關(guān)聯(lián)后,通過webhook就可以比較輕松的完成后續(xù)的工作,將每次的commit有效的關(guān)聯(lián)到我們的持續(xù)集成平臺的任務(wù)上來,通過閑魚CI工作平臺將日常打包自動化測試等流程變?yōu)樽詣踊男袨?,從而極大的減少了日常的工作。粗略統(tǒng)計下來,在去年自動化體系落地的過程中單就自動打Flutter包上傳以及觸發(fā)最終的App打包這一流程就讓每位同學(xué)每天節(jié)省一個小時以上的工作量,效果非常明顯。另外,基于代碼關(guān)聯(lián)需求的這套體系,可以相對容易的構(gòu)建后續(xù)的數(shù)據(jù)報表對整個過程和結(jié)果進行細化的分析,用數(shù)據(jù)驅(qū)動過程改進,保證新技術(shù)的落地過程Flutter的特性非常適合中小型客戶端團隊/Android市場占比較高的團隊/量產(chǎn)App的團隊。同時由于Flutter的特性導(dǎo)致閑魚團隊針對混合開發(fā)上的幾個典型問題提供了對應(yīng)的解決方案,使整個方案為全面推動Flutter在業(yè)務(wù)場景下的落地,閑魚團隊通過多次迭代演進出Fish新技術(shù)的落地過程中,在過程中通過數(shù)據(jù)化和自動化的方案極大的提升了過程除了本文提及的各種方案外,閑魚目前還在多個方向上發(fā)力,并對針對FlutterFlutter整個上層基礎(chǔ)設(shè)施的標準化演進,混合工程體系是否可以似Spring-boot的完整體系構(gòu)架,幫助更多的Flutter團隊解決上手動態(tài)性能力的擴展,在符合各應(yīng)用商店標準的情況下,助力業(yè)務(wù)鏈路的運營效率提升,保證業(yè)務(wù)效果。目前閑魚已有的動態(tài)化方案會后續(xù)作為Fish-ReduxFish-Redux+UI2Code,打通代碼生成鏈路和業(yè)務(wù)框架,保證在團隊標準統(tǒng)Flutter+FaaS,讓客戶端同學(xué)可以成為全棧工程師,通過前后端一體的架構(gòu)讓工程師去從事更多創(chuàng)造性的工作,是我們一直努力的目標。閑魚團隊也會在新的一年更多的完善Flutter體系的建設(shè),將更多已有的沉淀回饋給社區(qū),幫助FlutterFlutterPlugin調(diào)用NativeAPIsPackage,Flutter插件Flutter是Google使用Dart語言開發(fā)的一套移動閑魚開發(fā)Flutter過程中,經(jīng)常會需要各種Native的能力,如獲取設(shè)備信息、使用基礎(chǔ)網(wǎng)絡(luò)庫等,這時會使用Plugin來做橋接。本文將對Plugin進行詳細的介紹,希望能給Flutter開發(fā)者一些幫助。PlatformChannel進行了講解,隨后對“獲取剩余電量Plugin”進行了分解,最后1.FlutterPluginCupertino(iOS-style)風(fēng)格的Widgets,以及文本、圖片、按鈕等基礎(chǔ)Widgets;實際上,F(xiàn)lutter的上層能力都是Engine提供的。Flutter正是通過Engine將各個Platform的差異化抹平。而我們今天要講的Plugin,正是通過Engine提供的PlatformChannel實現(xiàn)的通信。2.PlatformChannel2.1FlutterApp調(diào)用NativeAPIs:通過上圖,我們看到FlutterApp是通過Plugin創(chuàng)建的PlatformChannel調(diào)用的NativeAPIs。2.2PlatformChannel架構(gòu)圖:iOSPlatform(Host),通過FlutterMethodChanPS:消息編解碼器,是JSON格式的二進制序列化,所以調(diào)用方法的參數(shù)類型必須是可JSON序列化的。FlutterAppDelegate,是iOS的Plugin管理器,它記錄了所有的Plugin,并3.獲取剩余電量Plugin3.1創(chuàng)建Plugin首先,我們創(chuàng)建一個Plugin(flutter_plugin_batterylevel)項目。Plugin(3)輸入Projectname和Projectlocation,Projecttype選擇“Plugin”;Projecttype:3.2PluginFlutter部分}}首先,我們實例_methodChannel(Channel名稱必須唯一),然后調(diào)用}}}}3.3PluginAndroid部分}}}(2)MethodChannel和EventChannel初始化的時候都需要傳遞Registrar,(4)設(shè)置EventChannel的Handler,即EventChannel.StreamHandler;EventChannel.StreamHandler實現(xiàn)EventChannel的Native調(diào)用FlutterApp。}}}}}}}}MethodCallHandler:EventChannel.StreamHandler:(1)publicvoidonListen(Objectobj,EventChannel.EventSinkeventSink);3.4PluginiOS部分}}iOS的Plugin注冊流程跟Android一致。只是需要注冊到AppDelegateFlutterMethodChannel和FlutterEventChannel被綁定到FlutterViewCon-}}}}4.加載PluginNativeAPIs,如“獲取剩余電量Plugin”。4.1將一個Package添加到FlutterApp中(2)運行flutterpackagesget,或者在IntelliJ里點擊PackagesGet;管理依賴有3種方式:Hostedpackages、Gitpackages、Pathpackages。4.2Hostedpackages(來自)4.3Gitpackages(遠端)如果你的代碼不經(jīng)常改動,或者不希望別人修改這部分代碼,你可以用Git來管理你的代碼。我們先創(chuàng)建一個Plugin(flutter_remote_package),并將它傳到Git4.4Pathpackages(本地)PS:如果你的代碼沒有特殊的場景需要,可以直接把Package放到本地,這樣我們在FlutterApp項目根目錄下(flutter_app),創(chuàng)建文件夾(plugins),然后5.踩過的坑5.1用XCode編輯Plugin我們已經(jīng)在pubspec.yaml里添加了依賴,但是打開iOS工程,卻看不到5.25.2iOS編譯沒問題,但是運行時找不到Plugin}[GeneratedPluginRegistrantregisterWithRegistry:self]默認注冊到self.5.3Native調(diào)用Flutter失敗這是因為PluginChannel的初始化大概要1.5秒,而且這是一個異步過程。雖然Flutter頁面顯示出來了,但是PluginChannel還沒初始化完,所以這時Native5.4iOSPlugin注冊到指定的FlutterViewController閑魚首頁是Native頁面,所以Window的rootViewController不是Flutter-ViewController,直接注冊Plugin會注冊失敗。我們需要將Plugin注冊到指定的我們需要在AppDelegate重寫上面兩個方法,方法內(nèi)返回需要指定的Flutter-調(diào)用NativeAPIs的應(yīng)用場景還是在Plugin方法調(diào)用過程中,可能會遇到傳遞復(fù)雜參數(shù)的情況(有時需要傳遞對象但是Plugin的參數(shù)是JSON序列化后的二進制數(shù)據(jù),所以傳參必須是可JSON序列化的。我覺得,應(yīng)該有一層對象映射層,來支持傳遞對象。因為后面會有Flutter視頻播放的專題文章《萬萬沒想到-Flutter這樣外接紋Flutter混合工程改造實踐閑魚技術(shù)團隊于2018年上半年率先引入了Flutter技術(shù)嘗試實現(xiàn)客戶端開發(fā)的卓工程都已相當(dāng)龐大,如何將Flutter無縫橋接到這些大工程并保證開發(fā)效率不受影本文針對項目實踐人員給出了一種通用的工程改造方案,希望為準備轉(zhuǎn)型Flutter的團隊提供參考。##問題Flutter的工程結(jié)構(gòu)比較特殊,由Flu很顯然,在擁有了Native工程的情況下,開發(fā)者不太可能去創(chuàng)建一個全新的Flutter工程重寫整個產(chǎn)品,因此Flutter工程將包含已有的Native工程,這樣就帶1)構(gòu)建打包問題:引入Flutter后,Native工程因?qū)ζ溆辛艘蕾嚭婉詈?,從而開始,執(zhí)行過程中包含了Native工程的構(gòu)建,開發(fā)者要配置完整的Flutter使用Native進行開發(fā),工程結(jié)構(gòu)的改動會使開發(fā)無法在純Native環(huán)境下進行,而適配到Flutter工程結(jié)構(gòu)對純Native開發(fā)來說針對以上問題,我們提出了以下的改造目標,力求最小化Native工程對Flutter1)Native工程可以獨立地編譯構(gòu)建和調(diào)試執(zhí)行,首先定義Native工程處于獨立目錄環(huán)境下稱為Standalone模式,處于Flutter目錄下稱為Flutter模式。目標中純Native開發(fā)或平臺打包就處于Standalone模式,F(xiàn)lutter對開發(fā)人員和打包平臺來說是透明的存在,不會影響構(gòu)建與調(diào)試;而Standalone模式對Flutt3)pubs插件目錄及用于索引的文件:Flutter下的插件,包括各種系統(tǒng)的和自定義的channels4)flutter_assets:Flutter依賴的靜態(tài)資源,如字體,圖片等1)本地依賴:通過修改Flutter構(gòu)建流程將其庫文件,源碼和資源直接放置到Native工程的子目錄中進行引用,以iOS為例,就是將Flu及相關(guān)插件等做成本地的pod依賴,資源也復(fù)制到本地進行維護。由此,Standalone模式便具備了獨立構(gòu)建和執(zhí)行的能力,對于純Native開發(fā)人員來說Flutter只是一些二方庫與資源的合集,無需關(guān)注。而在Flutter模式下,dart源碼的構(gòu)建流程不變,不影響編譯和調(diào)試;同時由于是本地依賴,F(xiàn)lutter模式下的各種改動也實時可以同步到Native工程的子目錄中,提交缺點:需要對Flutter原有的構(gòu)造流程進行稍嫌復(fù)雜的改動,并且與后續(xù)的2)遠程依賴:遠程依賴的想法是將Flutter所有依賴內(nèi)容都放在獨立的遠端倉庫中,在Standalone模式下引用遠程倉庫中的相關(guān)資源,源碼和庫文件,同步到Standalone模式方能生效。PS.閑魚最終選擇了這個策略。Flutter模式下父工程目錄下的ios和android的子目錄分別包含對應(yīng)的Native工程,代碼管理上子工程可以使用git的submodule形式,保證目錄間的獨立。###遠程依賴的實現(xiàn)在Standalone模式下,F(xiàn)lutter的依賴內(nèi)容都指向遠程倉庫中1)向Standalone模式同步Flutter的變更由于遠程依賴的問題是同步變動比較麻煩,為此閑魚開發(fā)了一系列腳本工具使該過程盡量自動完成。假設(shè)發(fā)生變化,那么在Flutter模式下構(gòu)建結(jié)束后,腳本會提取生成好的所有依賴文件拷貝到遠程倉庫,提交并打t然后依據(jù)打出的tag生成新的遠程依賴說明(比如iOS下的podspec最后在Standalone模式下修改Flutter的建議在提測及灰度期間,每次Flutter業(yè)務(wù)的提交都能夠觸發(fā)同步腳本的執(zhí)行和app打包;開發(fā)期間保持每日一次的同步即可。#總結(jié)為解決引入Flutter后的工程適配問題,我們抽取了Flutter的相關(guān)依賴放到遠程供純該方案已在閑魚施行了幾個版本,并反向輸出給了Flutter團隊,為其后續(xù)的hybrid工程組織計劃提供了方向和參考。同時,相信該方案也可以為轉(zhuǎn)型Flutter的團隊提供幫助,當(dāng)然項目間的閑魚Flutter混合工程持續(xù)集成的最佳實踐在之前的文章《Flutter混合工程改造實踐》中,有些同學(xué)留言想了解抽取Flutter依賴到遠程的一些實現(xiàn)細節(jié),所以本文重點來講一講Flutter混合工程中的2.思考(2)阿里集團的構(gòu)建系統(tǒng)目前并不支持直接構(gòu)建Flutter項目,這個也要求我們鑒于這兩點原因,我們希望可以設(shè)計一個Flutter依賴抽取模塊,可以將Flutter的依賴抽取為一個Flutter依賴庫發(fā)布到遠程,供純Native工程引用。如下3.實現(xiàn)3.1Native工程依賴的Flutter分析2.Flutter工程:我們自己實現(xiàn)的Flutter模塊功能,主要為Flutter工程下lib3.自己實現(xiàn)的FlutterPlugin:我們自己實現(xiàn)的FlutterPlugin。我們解開Android和iOS的APP文件,發(fā)現(xiàn)Flutter依賴的主要文件如下圖Android的Flutter依賴的文件:1.Flutter庫和引擎:/2.Flutter工程產(chǎn)物:isolate__snapshot__data、isolate__snapshot__instr、vm__snapshot__data、vm_snapshot_instr、flutter_assets。3.FlutterPlugin:isolate_snapshot_data應(yīng)用程序數(shù)據(jù)段isolate_snapshot_instr應(yīng)用程序指令段vm_snapshot_dataVM虛擬機數(shù)據(jù)段vm_snapshot_instrVM虛擬機指令段iOS的Flutter依賴的文件:1.Flutter庫和引擎:Flutter.f2.Flutter工程的產(chǎn)物:App.framework那我們只需要將這三部分的編譯結(jié)果抽取出來,打包成一個SDK依賴的形式提3.2Android依賴的Flutter庫抽取Flutter工程的Android打包,其實只是在Andro個flutter.gradle的任務(wù),而這個flutter.gradle主要做了三件事這個文件可以在3.插入Flutter工程的編譯任務(wù),最終將產(chǎn)物(兩個isolaate_snapshot文件、兩個vm_snapshot文件和flutter_assets文件夾)拷貝到mergeAssets.弄明白Flutter工程的Android編譯產(chǎn)物之后,因此我們對1.編譯Flutter工程。這部分主要工作是編譯Flutter的dart和資源部分,可以用AOT和Bundle命2.將flutter.jar和Flutter工程的產(chǎn)物打包成一個aar。}}}}}}}4.純粹的Native項目只需要compile我們發(fā)布到maven的aar即可。平時開發(fā)階段,我們需要實時能依賴最新的aar,所以我們采用SNAPSHOT}}}}}}3.3iOS依賴的Flutter庫的抽取1.獲取各種參數(shù)(如project_path,target_path,build_mode等主要來engine/{artifact_varian4.獲取生成App.framework命令所需參數(shù)(build_dir,local_engine_flag,preview_dart_2_flag,aot_flags)。5.生成App.framework,并將生成的App.framework和AppFramework-iOS的Flutter依賴的抽取步驟如下:1.編譯Flutter工程生成App.framework。這里主要有兩步:一是將plugin打成二進制3.將這些上傳到遠程倉庫,并生成新的Tag。4.純Native項目只需要更新pod依賴即可。4.Flutter混合工程的持續(xù)集成流程3.同時多條線并行開發(fā)Flutter時,版本管理混亂,容易出現(xiàn)遠程庫被覆蓋的其次,在開發(fā)測試階段,采用五段式的版本號,最后一位自動遞增產(chǎn)生,這樣就最后,在發(fā)布階段,采用三段式或四段式的版本號,可以和APP版本號保持一5.寫在最后構(gòu)建作為項目必須的第一步,很多團隊都有自己不同的模式和流程。但是基于混合Flutter的項目,F(xiàn)lutter混合構(gòu)建是無法越過的一步坎。所以可以借鑒本文思路,我們可以針對不同的標準制定個性化混合構(gòu)建流程,我們也開始嘗試將我們的構(gòu)建模式對接摩天輪中,變成一種集團標準打包模式。同時也歡迎和我們聯(lián)系討論FlutterFlutter新銳專家之路:工程研發(fā)體系篇當(dāng)前,閑魚客戶端已經(jīng)實現(xiàn)了基于Flutter的商品詳情頁的全量重構(gòu),線上效果良好。從alpha一路走來,我們遇到了很多問題,或基于原理方合作,都一個個解決了,是時候梳理和總結(jié)下,也希望為其他的開發(fā)者們,尤其是問題一個原因或解法的情況,而本系列的重點在于說明各種問題的解決方案與思路,就不一一列出問題。所有調(diào)試/熱重載相關(guān)的Flutter均為Debug模式的Flutter,本系列文章包含三篇:引入篇,運行篇,上線篇。引入篇重點介紹工程研發(fā)體系;運行篇介紹混合情景下的棧管理與能力補齊等;上線篇介紹兼容/穩(wěn)定性保障及已有的Native工程如何引入Flutter,工程結(jié)構(gòu)如何組織,如何管理Flutter環(huán)c.構(gòu)建優(yōu)化這里主要介紹如何去針對Flutter的工具鏈同d。混合工程下的Flutter研發(fā)結(jié)構(gòu)這部分的核心邏輯是如何在最小改動已有iOS/Android工程的前提下運行Flutter。我們可以將Flutter部分理解成為一個單獨的模塊,通過pod庫(iOS),aar庫(Android)的方式,由CocoaPods和Gradl具體的原理與實踐請參見:FlutterProjectStru編譯速度的優(yōu)化(Android)assembleDebug。a.修改flutter_tools.dflutter_tools_arguments_print.pngCommandLineApp,并基于步驟c獲得的入?yún)⑴渲谩盤rogramarguments”Dart-Command-Line-App-Flutter_Tools_Debuggingflutter_tools_debugger_frame_variablesNative視角下的Flutter調(diào)試iOS(Android)工程。對于Native背景的開發(fā)者來說,這不僅有些不適應(yīng),也常因雜。如何解決這個問題呢?這就涉及到Flutter視角和Native視角下的Flutter調(diào)試Flutter啟動下的Flutter的調(diào)試與熱重載邏輯a.檢查是否需要重新生成flutter_tools.snapshot。c.基于Flutter配置(如Framd.基于gradle和xcodebuild構(gòu)建應(yīng)用(Fluttef.等待應(yīng)用中Flutter啟動,尋找Observatory端口,通過DartDebuggerg.尋找
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年西安市雁塔區(qū)第一小學(xué)教師招聘備考題庫帶答案詳解
- 初中生物概念建構(gòu)中的多媒體資源運用與教學(xué)策略教學(xué)研究課題報告
- 2025年保定市寬高高級中學(xué)招聘備考題庫及答案詳解1套
- 2型糖尿病個體化治療藥物轉(zhuǎn)換策略
- 國網(wǎng)浙江電力2026年度高校畢業(yè)生招聘1170人備考題庫及一套參考答案詳解
- 2025年大連海事大學(xué)公開招聘事業(yè)編制非教學(xué)科研人員23人(第一批)備考題庫含答案詳解
- 2025年河南實達國際人力資源合作有限公司招聘宋城產(chǎn)投勞務(wù)派遣人員備考題庫有答案詳解
- 2025年連山教師招聘29人備考題庫完整參考答案詳解
- 2025年上海大學(xué)誠聘法學(xué)院院長備考題庫及答案詳解參考
- 簡約插畫風(fēng)深色年度晚會慶典
- 2025年榆林市住房公積金管理中心招聘(19人)備考筆試試題及答案解析
- 2025年金屬非金屬礦山(地下礦山)安全管理人員證考試題庫含答案
- 2025秋蘇教版(新教材)小學(xué)科學(xué)三年級上冊知識點及期末測試卷及答案
- 2025年及未來5年中國非晶合金變壓器市場深度分析及投資戰(zhàn)略咨詢報告
- 中文核心期刊論文模板(含基本格式和內(nèi)容要求)
- 2024-2025學(xué)年云南省普通高中高二下學(xué)期期末學(xué)業(yè)水平合格性考試數(shù)學(xué)試卷
- GB/T 18213-2025低頻電纜和電線無鍍層和有鍍層銅導(dǎo)體直流電阻計算導(dǎo)則
- 泰康人壽會計筆試題及答案
- 園林綠化養(yǎng)護項目投標書范本
- 烷基化裝置操作工安全培訓(xùn)模擬考核試卷含答案
- 汽車租賃行業(yè)組織架構(gòu)及崗位職責(zé)
評論
0/150
提交評論