关于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编程范式,能够看到头部公司基本上达成了共识,目前阶段,这就是最佳实践。