關於flutter的背景、體系結構、橫向對比等,建議閱讀淘寶的一篇文章,大比拼|下一代高性能跨平臺UI渲染引擎,人家是真的厲害。前端
這裏就很少貼這些宏觀的簡介了。本文主要從客戶端開發的角度看三個小點:線程、異步、聲明式UI,都是Flutter跟正常的客戶端開發有必定區別的地方。react
線程模型
線程模型這塊,大多數文章對初學者來說都有點不清不楚的,這裏詳細總結一下。android
首先,在上層flutter APP裏,咱們用dart語言開發,這個層面,是沒有線程的概念的,取而代之的是dart提供的相似線程的isolate。isolate簡單來說就是個受限制的線程,isolate之間只能經過一種叫port的消息機制通訊,不能共享內存。除此以外跟線程是同樣的。
dart vm默認提供了一個root isolate,有耗時操做須要執行時,能夠new出新的isolate執行。
Flutter engine這個層面,有四個Runner各司其職,這裏的Runner其實就是線程,不過這四個Runner是由Engine和Native之間的那個嵌入層去賦值的,engine層只會使用這四個Runner,不會建立新的線程。默認地,Platform Runner和Native的主線程是同一個線程。
回頭看dart的root isolate,它跟engine層的UI Runner是綁定的,即,它們兩個是同一個線程。編程
總體看一下,會發現一些特別的東西。對Flutter App來說,root isolate基本上能夠理解爲主線程,同時它也是UI線程。可是,它不是Native層面的主線程,在Native看來,它只是個子線程。網絡
對異步編程而言,客戶端開發最熟悉的多是callback語法,固然不少時候也會使用delegate。dart的callback語法以下:app
Timer.run(() => print('hi!'));
不過雖然dart也能夠用callback,可是更多的時候,會使用Future/async/await這套語法來執行異步任務。框架
Future<Response> dateRequest() async { String url = 'https://www.baidu.com'; Client client = Client(); Future<Response> response = client.get(requestURL); return response; } Future<String> loadData() async { Response response = await dataRequest(); return response.body; }
簡單看一下這個小例子,client.get()
是個異步的網絡請求,它能夠直接返回一個Future<Response>
的對象,這個名字頗有意思,它的意思是,我之後會給你個Response
類型的對象的,可是如今,只是個空頭支票(Future
)。異步
以後,可使用await關鍵字加上這個Future
,當前調用就會停在這裏,直到這個Future
對象返回纔會繼續向下執行。基本原理是,把當前上下文存到堆內存;當Future
返回時,會產生一個event進入eventloop(基本上是個語言都有這麼個玩意兒,能夠參考Dart與消息循環機制),這個event會觸發進入以前的上下文繼續執行。async
能夠看到,這裏的寫法很像同步的寫法,可是它是不會阻塞當前線程的,原理上面已經簡單解釋了。目前,async/await這種異步語法,是公認的異步語法的最佳方案。前端和安卓的kotlin已經比較普遍地使用了,而iOS還沒跟得上時代。ide
前面講Flutter線程模型時,已經提到了isolate。它在底層其實就是個線程,可是dart vm限制了isolate的能力,使得isolate之間不能直接共享內存,只能經過Port機制收發消息。
看一下代碼
void main() async{ runApp(MyApp()); //asyncFibonacci函數裏會建立一個isolate,並返回運行結果 print(await asyncFibonacci(20)); } //這裏以計算斐波那契數列爲例,返回的值是Future,由於是異步的 Future<dynamic> asyncFibonacci(int n) async{ final response = new ReceivePort(); await Isolate.spawn(isolateTask,response.sendPort); final sendPort = await response.first as SendPort; final answer = new ReceivePort(); sendPort.send([n,answer.sendPort]); return answer.first; } //建立isolate必需要的參數 void isolateTask(SendPort initialReplyTo){ final port = new ReceivePort(); //綁定 initialReplyTo.send(port.sendPort); //監聽 port.listen((message){ //獲取數據並解析 final data = message[0] as int; final send = message[1] as SendPort; //返回結果 send.send(syncFibonacci(data)); }); } int syncFibonacci(int n){ return n < 2 ? n : syncFibonacci(n-2) + syncFibonacci(n-1); }
Port分爲ReceivePort和SendPort,這二者是成對出現的,在新建一個isolate的時候,能夠傳入一個sendPort用於isolate向主線程發消息,若是主線程想往子線程發消息呢...就只能讓子線程new出一對port把sendport發過來才能用...
語法上是很囉嗦了,所幸Flutter給咱們封裝了便捷的compute函數,能夠參考深刻了解Flutter的isolate(4) --- 使用Compute寫isolates,因爲只是上層封裝,這裏就不具體展開了。
到這裏咱們基本上明白了,isolate就是個削弱版的線程,用起來麻煩一點,另外就是因爲不共享內存,port發送數據時是copy的,若是有大塊內存真的要copy多份,可能會有比較大的內存問題。
可是,官方明確說明,dart是個單線程語言,這怎麼理解呢?仍是要回到isolate和線程的區別。因爲isolate之間是不共享內存的,它們其實基本上是徹底隔離的。隔離就是這裏的關鍵,從上層代碼來看,是的,個人代碼開了好幾個線程,可是,從執行邏輯上,每一個isolate直接是相互隔離的,對每一個isolate內的邏輯來說,它就是單線程的。
聲明式UI與響應式UI是對應的概念,考慮一下iOS/android的UI實現。
iOS是很純粹的命令式,new view,addsubview,new view,addsubview,這樣搞。
安卓呢,算是半命令式吧,xml聲明瞭UI,這是聲明式的部分;但程序運行時若是要修改某個view,還是取到這個view,再去命令式地修改。
下面來看看flutter的框架
flutter的UI框架吸收了react的理念,即 UI是關於狀態的函數。
具體看一下demo
class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), leading: IconButton(icon:Icon(Icons.arrow_back), onPressed:() => SystemNavigator.pop(), ) ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } }
這個是官方的helloworld demo。每一個組件,會有個build函數,這裏會返回一個可以完整描述UI的對象結構。每當數據改變時,就從新調用build函數,返回新的結構。如何高效渲染,就是框架去作的事情了。
經過這種方式,不論是UI的初始佈局結構,仍是後面的修改,都是build函數返回的對象結構去聲明的,完整的聲明式UI由此而來。
UI開發的最佳實踐是怎麼樣的,一直以來都充滿爭議。但近幾年,React -> Flutter -> SwiftUI,都使用了聲明式的UI編程範式,能夠看到頭部公司基本上達成了共識,目前階段,這就是最佳實踐。