關於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看來,它只是個子線程。bash
對異步編程而言,客戶端開發最熟悉的多是callback語法,固然不少時候也會使用delegate。dart的callback語法以下:網絡
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會觸發進入以前的上下文繼續執行。app
能夠看到,這裏的寫法很像同步的寫法,可是它是不會阻塞當前線程的,原理上面已經簡單解釋了。目前,async/await這種異步語法,是公認的異步語法的最佳方案。前端和安卓的kotlin已經比較普遍地使用了,而iOS還沒跟得上時代。框架
前面講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實際上是內核級線程的封裝,在系統內核層面就是多線程的。
我以爲這句話可能從理論模式上理解會比較好。
併發編程長期以來有兩種範式,一種是基於共享內存的,主要是多線程編程;一種是基於消息的,如Actor、CSP模型。從這個角度看,isolate實際上是消息驅動的併發編程,算是CSP模型的簡化,跟多線程編程是徹底不一樣的併發編程範式。
所以說dart是個單線程語言也是說得通的。
聲明式UI與響應式UI是對應的概念,考慮一下iOS/android的UI實現。
iOS是很純粹的命令式,new view,addsubview,new view,addsubview,這樣搞。
安卓呢,算是半命令式吧,xml聲明瞭UI,這是聲明式的部分;但程序運行時若是要修改某個view,還是取到這個view,再去命令式地修改。
下面來看看flutter的框架
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編程範式,能夠看到頭部公司基本上達成了共識,目前階段,這就是最佳實踐。