Flutter项目如何将任务转移到后台线程?
Android 中,当你想要访问一个网络资源却又不想阻塞主线程并避免 ANR 的时候,你一般会将任务放到一个后台线程中运行。例如,你可以使用一个 AsyncTask、一个 LiveData、一个 IntentService、一个 JobScheduler 任务或者通过 RxJava 的管道用调度器将任务切换到后台线程中。
由于 Flutter 是单线程并且运行一个事件循环(类似 ),你无须担心线程的管理以及后台线程的创建。如果你在执行和 I/O 绑定的任务,例如存储访问或者网络请求,那么你可以安全地使用 async/await,并无后顾之忧。再例如,你需要执行消耗 CPU 的计算密集型工作,那么你可以将其转移到一个 Isolate 上以避免阻塞事件循环,就像你在 Android 中会将任何任务放到主线程之外一样。
对于和 I/O 绑定的任务,将方法声明为 async 方法,并在方法内 await 一个长时间运行的任务:
content_copy
loadData() async {
String dataURL = "";
http.Response response = await http.get(dataURL);
setState(() {
widgets = ();
});
}
这就是你一般应该如何执行网络和数据库操作,它们都属于 I/O 操作。
在 Android 中,当你继承 AsyncTask 的时候,你一般会覆写三个方法,onPreExecute()、doInBackground() 和 onPostExecute()。Flutter 中没有对应的 API,你只需要 await 一个耗时方法调用, Dart 的事件循环就会帮你处理剩下的事情。
然而,有时候你可能需要处理大量的数据并挂起你的 UI。在 Flutter 中,可以通过使用 Isolate 来利用多核处理器的优势执行耗时或计算密集的任务。
Isolate 是独立执行的线程,不会和主执行内存堆分享内存。这意味着你无法访问主线程的变量,或者调用 setState() 更新 UI。不同于 Android 中的线程,Isolate 如其名所示,它们无法分享内存(例如通过静态变量的形式)。
下面的例子展示了一个简单的 Isolate 是如何将数据分享给主线程来更新 UI 的。
content_copy
loadData() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, );
// The 'echo' isolate sends its SendPort as the first message.
SendPort sendPort = await receivePort.first;
List msg = await sendReceive(sendPort, "");
setState(() {
widgets = msg;
});
}
// The entry point for the isolate.
static dataLoader(SendPort sendPort) async {
// Open the ReceivePort for incoming messages.
ReceivePort port = ReceivePort();
// Notify any other isolates what port this isolate listens to.
sendPort.send();
await for (var msg in port) {
String data = msg[0];
SendPort replyTo = msg[1];
String dataURL = data;
http.Response response = await http.get(dataURL);
// Lots of JSON to parse
replyTo.send(());
}
}
Future sendReceive(SendPort port, msg) {
ReceivePort response = ReceivePort();
([msg, ]);
return response.first;
}
这里的 dataLoader() 就是运行在自己独立执行线程内的 Isolate。在 Isolate 中你可以执行更多的 CPU 密集型操作(例如解析一个大的 JSON 数据),或者执行计算密集型的数学运算,例如加密或信号处理。
你可以运行下面这个完整的例子:
content_copy
import 'dart:convert';
import 'package:flutter/';
import 'package:http/' as http;
import 'dart:async';
import 'dart:isolate';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: ,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() =< _SampleAppPageState();
}
class _SampleAppPageState extends State>SampleAppPage< {
List widgets = [];
@override
void initState() {
();
loadData();
}
showLoadingDialog() {
if (widgets.length == 0) {
return true;
}
return false;
}
getBody() {
if (showLoadingDialog()) {
return getProgressDialog();
} else {
return getListView();
}
}
getProgressDialog() {
return Center(child: CircularProgressIndicator());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: getBody());
}
ListView getListView() =< ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
});
Widget getRow(int i) {
return Padding(padding: (), child: Text("Row ${widgets[i]["title"]}"));
}
loadData() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, );
// The 'echo' isolate sends its SendPort as the first message
SendPort sendPort = await receivePort.first;
List msg = await sendReceive(sendPort, "");
setState(() {
widgets = msg;
});
}
// the entry point for the isolate
static dataLoader(SendPort sendPort) async {
// Open the ReceivePort for incoming messages.
ReceivePort port = ReceivePort();
// Notify any other isolates what port this isolate listens to.
sendPort.send();
await for (var msg in port) {
String data = msg[0];
SendPort replyTo = msg[1];
String dataURL = data;
http.Response response = await http.get(dataURL);
// Lots of JSON to parse
replyTo.send(());
}
}
Future sendReceive(SendPort port, msg) {
ReceivePort response = ReceivePort();
([msg, ]);
return response.first;
}
}







