演講實錄——Flutter-閑魚的探索與收獲 [復制鏈接]

2019-4-29 10:48
九霄逆鱗 閱讀:365 評論:0 贊:0
Tag:  
本文來自2019安卓巴士開發者大會現場實錄,由于錄入匆忙,內容可能存在偏差,歡迎大家掃描文末二維碼查看現場實錄視頻和下載大會完整PPT。作者:熊華麗

大家下午好!我來自閑魚的匠修。我本名叫熊華麗,我在阿里的化名叫匠修。我自己是做安卓系統出身的,我在深圳做了5年的安卓系統開發,又去了杭州阿里巴巴,然后在閑魚做了3年的客戶端開發,今天也算是重新回到深圳了,我對大家感覺非常親切。我看到大家這么多人在一起,我又感覺到非常幸福。今天是星期六,大家不用去加班,還要過來這里。我們沒有996,也沒有去ICU,我覺得我們這些人應該是在中國幸福感爆棚的一群人。Flutter是當下比較熱門的跨端開發技術,因為它使用了前期的跨端思路,它的前途是一片向好的。閑魚在很早期的時候就開始關注Flutter的發展,我們在非常早期的時候,可能Flutter連第一個版本都沒發出來,我們就跟谷歌的官方團隊建立合作,我們也一直跟谷歌的官方團隊Flutter保持了很好的技術溝通。現在Flutter在閑魚已經成長為了可以支持核心業務的主力的技術棧。

閑魚在做Flutter的實踐中分為三個階段。第一階段,前期我們需要對Flutter做一些技術棧的驗證,我們看這個技術棧是否能夠達到工業開發的標準,后來我們又需要對Flutter和原來的技術體系做融合,做能力上做雙向打通。后面大量使用Flutter的時候,我們又需要對Flutter做基礎建設,來提高日常開發的效能。閑魚在這個過程中做了非常多的事情,今天我在這里不可能把畢生絕學在一個小時之內傳給大家,這不太可能。就是說,我能不能順著閑魚的實踐過程,從里面摘出幾個比較有典型意義的代表問題,然后跟大家分享一下閑魚解決問題的思路和一些方案。Flutter首先是一個新的跨平臺技術棧,我們知道,它確實能夠提高開發效率,它也不是萬能的,只是它作為一個技術有自己的優點,也會有自己的缺點,我們在使用Flutter之前應該去了解一下,去考察一下Flutter的技術細節,和原來的技術去做對比。

(PPT圖示)左邊是Flutter大概的架構圖,它可以分為兩層,一個是底下的C++實現的Engine層面運行與檢索,還有上面用Dart實現的Framework。Flutter就是通過這兩層,它僅僅借用平臺原生的圖形渲染能力,使用定制的運行引擎和UI Widget,在底層實現跨端。這種做法的技術成本是很高的,有可能只有像谷歌這樣的爸爸才能做得出來。因為它采用這種做法,它有自定義的Engine和Framework,所以它在在技術上可以做到非常深度的優化,所以它的運行性能就非常好。根據閑魚的實踐統計的數據和我們做的測試,我們可以看一下Flutter和當下的技術棧的對比。首先,Native擁有最好的用戶體驗,它的開發成本也是最高的。H5大幅降低了開發成本,同時也犧牲掉了用戶體驗。RN就是在H5的輸入上,對用戶體驗做了優化,但我們覺得它依然不是很完善,因為RN最近好像發展得不是很順利。Flutter既有一個比較好的開發效率,幾乎可以媲美Native的運行性能,它的用戶體驗非常好,非常適合用來對開發效率對跨端有要求,同時它又要保持有良好用戶體驗的場景。在閑魚里,我們通常使用Flutter來開發我們的主流業務。

整個閑魚是一個非常大的APP,里面不僅只有Flutter,也會有一部分Native開發,也會有H5的開發,我們也引入了集團的Widget開發,類似于RN,還有Flutter開發。就是說,現在我們有4套技術棧。我們的想法非常簡單,我們會在具體的業務場景下選擇最合適的技術棧,充分利用這些技術棧各自的優勢做到取長補短,相互補充。我們早期對Flutter做了調研之后,發現Flutter可以對整個閑魚的技術結構體系做一個很好的補充。把Flutter引入到我們的工作中,需要把Flutter和原來的技術能力做融合。在融合的過程中我們發現了一些問題。

大家如果入門Flutter的話,僅僅使用Flutter的技術去編程的話,你想要在Flutter頁面打開一個原生的頁面,你會發現好像沒這個能力。這個問題很容易解決,你可以直接到Flutter Pages里面找一個插件,有很多這樣的插件,就可以提供這樣的能力。這從層面反映了,現在Flutter的頁面和原生的頁面之間還是存在一定隔閡的。Flutter的技術淵源,它和WEP技術有非常深的淵源。我們在理解Flutter的時候,可以把Flutter類似于大幅優化的做法,它在平臺中的運用是非常靈活的。現在閑魚的首頁有這樣的場景,有4個Tap(音),下面有個Tap就是社區。我們引入Flutter之后就想,能不能把它改成Flutter的開發,然后改善客戶體驗。我們研究了一下發現,Flutter提供的能力,現在是把它放在Flutter里面打包給你,要不你就不用。它是不能做到混合的,比如部分使用Flutter,另外一部分還保持原生,Flutter現在還沒有這個能力。在閑魚里面就會出現下面這個圖像(PPT圖示),它會出現在這個頁面棧里面,Flutter的頁面和普通的頁面會交疊出現,它會對接很多的頁面棧,這對內存的使用是一個很大的壓力。其實這不光光對內存使用是很大的壓力,它限制了很多優化的場景。比如一個Flutter頁面,如果它被另外一個Flutter頁面蓋住的時候,這個Flutter頁面對用戶來說是不可見的,我們可以把被蓋住的頁面里面很多資源釋放掉,正在加載的資源也可以把它做一個暫停,因為它也不可見了。但是如果每一個Flutter頁面都是放在各自的環境里面,它在自己的環境里都是屬于最上層的頁面,那我們就沒辦法去做這樣的優化了。基于此,閑魚建設了一個基礎設施,叫混合棧。如果將Flutter引入到整個工程中來,最起碼還要分為兩批頁面,一批是Flutter頁面,一批是native頁面。要實現這兩個頁面之間協調調動,實現混合使用,實現業務功能的話,中間一定要有一個調動的邏輯層,整個混合棧就是一個混合調動的邏輯層。

混合棧提供三個能力:第一,最基礎的能力,要把Flutter頁面無差別接入到本地頁面的路由系統里,本地的其他頁面就可以很好地通過本地路由系統去打開Flutter頁面,它就可以很好的在業務實現上做很好的協同。第二,需要補充Flutter嵌入的開發能力。谷歌已經意識到這個問題,也往這方面做優化。我們在建設的這個東西也一直在跟進,如果谷歌放出這方面的能力,我們后面可能會把谷歌的能力很快補充進來。第三,你要統一調度這兩種頁面棧的話,它是一個復雜的邏輯,整個混合棧需要對這些邏輯進行分裝,要對Flutter和Native交疊出現的場景做必要的優化,整個產品才能達到上線的標準。混合棧在閑魚里已經做了兩個版本的迭代。經過第一個版本的實踐之后發現,如果要協同調度好兩個頁面棧,并不是一件非常容易的事情。在安卓里面各個頁面棧是一件非常復雜的事情,在安卓里一個APP可以起多個頁面連接棧,在棧中每次打開的時候都有4種打開模式,它的行為是非常復雜的,因為我們還要支持后面Flutter的頁面模式,所以它整個邏輯是非常復雜的。在后期開發的時候,為了簡化這種邏輯,我們在做Flutter部署的時候要確定一些原則,所有的Flutter環境中的頁面棧行為,都必須通過本地去驅動它。也就是說,要通過Native里面的生命周期去驅動容器里Flutter頁面的生命周期。打個比方,比如說我現在有個Flutter頁面在跟用戶交互,當用戶點擊back鍵的時候,Flutter頁面理論上馬上就要top(音),如果用混合棧的話,它不能自己去調度,必須把這個團傳遞到混合棧里面去,混合棧會再去驅動里面的Flutter頁面,通過這樣一種邏輯,有這樣的原則的話,后面做很多功能開發,做能力拓展的時候,就不至于把這個邏輯搞得特別復雜,因為搞復雜之后你可能自己都理不清楚它們之間雙向的驅動關系。這樣做的前提是此,要實現Activity和Flutter中的頁面,要實現在邏輯上的一一映射。

經過一段時間的實踐之后,在做Flutter部署里面,總結出Flutter混合棧實現的幾個難點,也可以把它講成混合棧實現的幾個要點。第一,不侵入官方的SDK。這也是我們的一個教訓,我們在第一版本的時候是通過修改官方SDK中的源碼去獲取部分能力,我們在做這個事情的時候,有部分能力確實需要官方SDK提供,但是官方SDK沒有往這方面做過多的考慮,所以很多功能沒有提供得很好。我們在做第二版的時候非常克制,嚴禁動官方SDK的源碼,這樣當官方SDK升級的時候我們可以跟著升級,就不至于在升級的時候落后太多的版本。這樣做有一個好處,官方SDK中API的語義不要去動它,我們可以對這個語義做包裹或做增強,但是不能改變它原來基礎的語義,開發者在做開發的時候就不需要去賦能,對開發者做一個透明。第二,這是一個小坑,我們要實現Flutter頁面的生命周期和Native的生命周期做很好的互通和兼容,它很容易產生坑。打開一個新的Activity,我們通常會走Opors(音),然后再走一下Ostop(音)。但是在有些情況下,比如設置了特殊的主題或者有些特殊的場景下,它可能僅僅會走一個Opors生命周期,它不會走到Ostop里面,在實現的時候對這種場景要有充分的考慮。第三,因為Flutter現在還不能很好地深度使用,我們要把Flutter6的一些環境從Flutter Activity中剝離出來,然后做一個標準運行容器的定義,要做一個基礎的分裝給到開發者,這樣開發者才能非常靈活去是否Flutter的能力,他可以把Flutter當作Activity的一部分去嵌入使用。第四,我們要實現前面講的原則,就是說,所有Flutter層的頁面棧行為都要用Native層去驅動,這有一個前提,所有Flutter頁面和本地容器的Activity都有一個邏輯上的映射關系,但整個安卓APP的棧管理非常復雜,它的數據結構非常復雜,它可能不是一個單純的棧的數據結構,因為每個APP運行的時候可以起多個test的棧,每個Activity的行為都很復雜,因為它有4種啟動模式,它不是一個簡單的行為,你打開Activity的時候有可能這個Activity會新起一個頁面棧,有可能這個Activity會復用老的頁面棧的實例,它有可能會在前面的棧里面找到這個Activity,然后把上面這些Activity全部出棧,整個行為很抽象也很復雜。Flutter是對這種行為的特別延展,它的抽象很簡單,就是一個簡單的棧,它的行為也可以簡單獲取。我們研究了一段時間的Flutter代碼之后,發現Flutter默認有個棧管理的能力,簡單的頁面棧行為管理的能力,但是它又允許你自己實現頁面棧管理的能力,我們在Flutter實現了我們想要的頁面棧管理的能力,通過這樣來實現邏輯上的一一映射關系。

看一下這個代碼,是Flutter部署初始化的代碼。最關鍵的用紅線標出來。一般Flutter app都會采用安卓Material的模式,這個方法引起你注入頁面管理,定制自己的頁面管理行為。我們現在已經實現了本地技術棧能力和Flutter技術棧的融合,在能力上就能做到相互打通,就可以開發業務了。閑魚在最開始的時候是用Flutter重構了寶貝詳情頁,當時有3個同事,請了一個項目去做這個事情,做完之后就解散。后面有新的同學進來,看到前面同學的代碼,第一感覺就跪掉了。他們的代碼各有各的風格,很多功能組件都很模糊,可能數據放在最外層,界面在非常深的層次上,組建也提得不標準,很難把以前的工作運用起來。通過以前的開發經驗我們知道,面對這種場景,非常有必要給業務開發同學做一個開發框架,有了這個開發框架之后,業務開發人員對做開發代碼有一個大約的規約,我們會達成一個共識,寫代碼的風格也是類似的,邏輯都是共享的,可以加速以后的業務開發。

我們做這個事之前,先來看一下現在Flutter的業界,就是Flutter的社區里都有哪些業務框架可以供我們考察。第一個是scoped 業務框架,非常容易上手,它是利用Flutter的特性,實現簡單的數據監聽模式,有基礎的數據關系和數據流。大家要研究Flutter的業務框架數據的話,先通過這個框架做入門是非常好的選擇。這個框架的抽象能力不高,也很難支持復雜業務場景。第二個是BLoC,響應式流式編程,,清晰的數據流,非常值得大家去借鑒了解。這種編程方式能夠帶來非常清晰的邏輯,也能帶來很清晰的數據流。第三是Flutter-Redux,是閑魚在早期使用的業務開發框架。Redux有一個好處,有一個統一的狀態管理,就是對數據處理的邏輯,對整個業務邏輯做了很好的規范定義,它把需要改變數據同步的邏輯想象成Redux,你在開發業務邏輯的時候很簡單,它本身相當于一個邏輯框架,搭建好了,你做一個填空就好了。這就是一個標準的邏輯了。它做得非常好。Redux使用了action的方式,一般和用戶進行交互的時候,會發出一個action,表達用戶交互的意圖,至于這個意圖會帶來哪些變化,界面里是完全不關心的。通過這兩個,Redux非常適合大規模的比較復雜的頁面編程。

閑魚經過一段時間的實踐,基于Flutter Redux做了挺多個版本的迭代,最后發現,社區很多框架里面,對頁面內的復雜度預期不足,在閑魚的場景下,一個核心的主頁面可能邏輯非常復雜。閑魚總結出了我們自己需要的四個能力,就是需要編程框架提供這四個能力。第一個是統一狀態管理,可以對邏輯做標準化規范,有利于寫代碼員的共同。第二個是分治能力,非常有利于大型團隊進行協同,可以同時有四五個同學來開發一個頁面,再把它整合起來,效率很高。第三個是復用,是加速開發的方法。第四是編程模版,寫出來的代碼風格是類似的。基于這四個需求,閑魚在后期開發了自己的業務模型框架,就是fish-redux。這是社區標準的Redux概念,如果大家熟悉Redux的話不用擔心,就是你們熟悉的原汁原味的Redux,我們沒有做破壞。它提供一個Component層,就是一個組件。還有一個Adapter層,這是一種高階的進階。

Component是我們設計的核心,我就介紹一下這個Component。我們對Component的定義,它是一個最小的業務單元,有自己的數據、邏輯,在某種情況下是一個自閉單元。Stateful是一個常用的組合,在安卓里面可以用。
你可以通過d在交互的時候發出一個action的數據。也可以通過S去監聽,提供頁面數據的連接器,從頁面的數據里把自己要的數據取到。整個組件里是有邏輯的,需要改變數據的同步的邏輯,把它放在Redux里面。最后還有一個非常重要的是Slods,是組件插槽。通常有些組件并不關心具體的業務實踐,相當于定義接口,類似于細節類的方式,把具體要實現的功能點通過差點的方式流出去,自己去關注怎么組合起來,通過叫做模版組件。

我們大概看一個例子,就是閑魚的寶貝詳情頁。首先看這個頁面,最大的就是Component,外層的Component不關注具體的業務,會根據不同的數據去組合出不同的寶貝詳情頁來,具體的功能流給下一層的Component實現,比如有視頻的Component,有留言等。通過這種方式就可以很好地實現分治和復用。我們在開發新頁面的時候,通常可以用到老頁面里面的很多元素,我們只需要對某些特定的Component去實現就夠了,可以利用老的Component,通過新的Component來組合它,加速開發。

這就是閑魚里組件的代碼樣圖,代碼看起來風格都非常像素,組成要素也差不多,你寫得好與不好我心里也是有一根秤的。每一次產品上線都是很大的考驗,你做了很多前面的準備,也不敢保證完全不出問題。所以我們團隊里經常調侃:人在家中坐,鍋從天上來。在數據運行的時候,需要對線上運行的質量狀態做質量監控,再做對比,就知道技術的點優化得是否可以達到預期的效果,后面還需要做什么。整個數據監控是非常關鍵的。在阿里巴巴數據是一種信仰,如果沒有數據,什么都面談。對于產品監控的數據多多益善。

分享一下在Flutter下面數據監控的維度和收集的方法。第一個最關鍵的是線上運行穩定性監控,在Flutter里面,信息怎么拿到呢?有三個方法,第一,這個框架里提供了默認的異常處理函數,我們可以對這個處理函數做包裹,在包裹的代碼里上傳信息的代碼。第二,Flutter是運用信息開發的,它的本質是不共享內存的,它是放到一個個Slods里面運行的,可以拿到它的運行信息。第三,Flutter是在冪函數里進行的,它給你提供了ol的回調。第一種方式拿到的信息是最全的,后面兩種方式可以作為補充。第二,性能監控。也很關鍵,因為你上線了Flutter之后,等到用戶再去罵娘的時候,可能很多用戶已經把這個APP卸載了。我們有一個線上的監控體系,有一些指標,有滑動,頁面加載時長等。

Flutter分為兩層,這兩層有代碼的交付層,后面做開發會遇到B。我們采取一種方案,可以在運行的很早期把這個框架建起來,通過這種方法可以拿到一些生命周期的回調。可以通過這兩個去看一下增率的情況。前面講了很多Flutter的問題,怕給大家帶來一個印象,Flutter好像坑太多了,我還是不搞算了。我自所以講這些問題,因為時間很短,我不可能把很多事情講得面面俱到,我又不想講概念,想講我們的一些思考。Flutter整體用起來是一個非常成熟的框架,但是如果想接入一個新的技術棧,完全一點坑都不踩是不可能的。如果在開發的時候感覺有一些能力沒有,可以進入到Flutter的Pub里面去找一找。如果你想進階成為Flutter的高手也很好,可以去github里面看一看。

閑魚走得比較早,我們很早和Flutter官方團隊有一些合作,對于一些遇見的問題都有一些方案了,我們在社區里會把這些方案做分享,給后面的同學做借鑒和參考。因為Flutter現在還是不支持反射的,所以有一些賬號還是不好做的。我們現在用Flutter的業務開發框架,其實已經把它開發出來了。還有很多數據收集、高可用的框架,后面做整體之后也會把它開源出來。閑魚還是非常想在Flutter社區和大家一起攜手并進,通過新的技術為客戶端技術提供新的可能。大家如果感興趣可以關注閑魚技術的微信公眾號,謝謝大家!

flutter_boost: https://github.com/alibaba/flutter_boost
fish_redux: https://github.com/alibaba/fish-redux

現場PPT分享:       
關注【安卓巴士Android開發者門戶】公眾號,后臺回復“420”獲取講師完整PPT。


大會現場視頻小程序:


我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

两码中特期期