之前在yi提到过,Dart是单线程执行模型。但是它支持Isolate(一种让 Dart 代码运行在其他线程的方式)、时间循环和异步编程。除非自己创建一个Isolate,否则Dart代码会运行在主线程,并由event loop驱动。
Dart的单线程模型并不意味着一定是阻塞操作。可以使用Dart提供的异步工具(async/await),来实现异步操作。
实现一个列表,数据请求后刷新界面,需要在pubspec.yml
中添加http:
引入该http包:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class AsyncSampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
theme: ThemeData(
primarySwatch: Colors.blue
),
home: AsyncSampleAppHomePage(),
);
}
}
class AsyncSampleAppHomePage extends StatefulWidget {
AsyncSampleAppHomePage({Key key}) : super(key: key);
_AsyncSampleAppHomePageState createState() => _AsyncSampleAppHomePageState();
}
class _AsyncSampleAppHomePageState extends State<AsyncSampleAppHomePage> {
List widgets = [];
@override
void initState() {
// TODO: implement initState
super.initState();
loadData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('异步'),
),
// 创建一个ListView
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position){
return getRow(position);
},
),
);
}
// 返回一个列表子元素,类似UITableViewCell
Widget getRow(int i) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Text('Row ${widgets[i]["title"]}'),
);
}
// 网络请求,异步
void loadData() async {
String dataUrl = 'https://jsonplaceholder.typicode.com/posts';
// 获取到数据后解析
http.Response response = await http.get(dataUrl);
setState(() {
widgets = json.decode(response.body);
});
}
}
由于Flutter是单线程并且跑着一个event loop的,所以不必为线程管理或是开启后台线程而操心。如果正在做I/O操作(访问磁盘或网络请求),使用async/await就可以。如果让CPU执行计算密集型任务,需要使用Isolate来避免阻塞event loop。
在Flutter中,使用Isolate来发挥多核CPU的优势来处理长期执行的或计算密集型的任务。Isolates是分离的运行线程,并且不和主线程的内存堆共享内容,所以不能访问主线程中的变量或者使用setState()来更新UI。Isolates不能共享内存。
使用Isolate更新UI示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
class IsolateSampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: IsolateSampleAppPage(),
);
}
}
class IsolateSampleAppPage extends StatefulWidget {
IsolateSampleAppPage({Key key}) : super(key: key);
_IsolateSampleAppPageState createState() => _IsolateSampleAppPageState();
}
class _IsolateSampleAppPageState extends State<IsolateSampleAppPage> {
List widgets = [];
bool loading = true;
@override
void initState() {
// TODO: implement initState
super.initState();
loadData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Isolate'),
backgroundColor: Colors.blue,
),
body: getBody(),
);
}
showLoadingDialog() {
return loading;
}
getBody() {
if (showLoadingDialog()) {
return getProgressDialog();
}
return getListView();
}
getProgressDialog() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget getListView() {
return ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
},
);
}
Widget getRow(int i) {
return Padding(
padding: EdgeInsets.all(10),
child: Text(widgets[i]['title']),
);
}
loadData() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
// The 'echo' isolate sends its SendPort as the first message
SendPort sendPort = await receivePort.first;
List msg = await sendReceive(sendPort, 'https://jsonplaceholder.typicode.com/posts');
setState(() {
widgets = msg;
loading = false;
});
}
// 进入Isolate
static dataLoader(SendPort sendPort) async {
// 打开接收消息端口
ReceivePort port = ReceivePort();
// 通知Isolate
sendPort.send(port.sendPort);
await for (var msg in port) {
String data = msg[0];
SendPort replyTo = msg[1];
String dataUrl = data;
http.Response response = await http.get(dataUrl);
replyTo.send(json.decode(response.body));
}
}
Future sendReceive(SendPort port, msg) {
ReceivePort response = ReceivePort();
port.send([msg, response.sendPort]);
return response.first;
}
}