Flutter中的runOnUiThread()相当于什么?
Dart具有单线程执行模型,支持Isolate
(一种在另一个线程上运行Dart代码的方法)、事件循环和异步线程。除非你生成一个Isolate
,否则您的Dart代码将在主UI线程中运行并由事件循环驱动。Flutter的event loop相当于Android的main Looper
——依附于主线程的Looper
。
Dart的单线程模型并不意味着你需要将所有内容作为导致UI冻结的阻塞操作来运行。与Android要求你始终保持主线程空闲不同,在Flutter中,使用Dart语言提供的异步,例如async/await
,来执行异步工作。async
,如果用过C#、Javascript中使用过await
,或者您使用过Kotlin的协程,您可能会熟悉它。
例如,你可以通过使用async/await
并让Dart完成繁重的工作来运行网络代码而不会导致UI挂起:
Future<void> loadData() assync {
var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
http.Response response = await http.get(dataURL);
setState(() {
widgets = jsonDecode(response.body);
});
}
await
网络调用完成后,通过调用更新UIsetState()
这会触发小部件树的重建并更新数据。
以下示例异步加载数据并显示在ListView
:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
@override
void initState() {
super.initState();
loadData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (context, position) {
return getRow(position);
},
),
);
}
Future<void> loadData() async {
var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
http.Response response = await http.get(dataURL);
setState(() {
widgets = jsonDecode(response.body);
});
}
Widget getRow(int position) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Text('Row ${widgets[position]["title"]}'),
);
}
}
image.png
你如何将工作转移到后台线程?
在Android中,当你想要访问网络资源时,你通常会移至后台线程并完成工作,以免阻塞主线程并避免ANR。例如,你可能正在使用一个AsyncTask
、一个LiveData
、一个IntentService
、一个JobScheduler
作业或一个RxJava管道以及一个在后台线程上运行的调度程序。
由于Flutter是单线程并且运行一个事件循环(如Node.js),您不必担心线程管理或生成后台线程。如果您正在进行I/O密集型工作,例如磁盘访问或网络调用,那么您可以安全地使用async/await
并且一切就绪。另一方面,如果您需要执行使CPU忙碌的结算密集型工作,您希望将其移至一个Isolate
以避免阻塞事件循环,就像在Android中将任何类型的工作排出在主线程之外一样。
对于I/O密集型工作,将函数声明为async
函数,并await
在函数内部长时间运行的任务:
Future<void> loadData() async {
var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
http.Response response = await http.get(dataURL);
setState(() {
widgets = jsonDecode(response.body);
});
}
这就是您通常进行网络或数据库调用的方式,它们都是I/O操作。
在Android上,当您继承AsyncTask
,通常会重写3个方法onPreExecute()
,doInBackground()
和onPostExecute()
。在Flutter中没有等价物,因为你await
在一个长时间运行的函数上,Dart的书剑循环会处理剩下的事情。
但是,有时您可能正在处理大量数据并且您的UI挂起。在Flutter中,使用Isonlate
可以利用多个CPU内核来执行长时间运行或计算密集型任务。
Isolate是独立的执行线程,不与主执行内存堆共享任何内存。这意味着您无法从主现成访问变量,或通过调用setState()
。与Android线程不同,Isolate名副其实,不能共享内存(例如,以静态字段的形式)。
下面的示例在一个简单的Isolate,展示了如何将数据共享会主线程以更新UI。
Future<void> loadData() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
// The 'echo' issolate 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;
});
}
// The entry point fot the isolate
static Future<void> dataLoader(SendPort sendPort) async {
// Open the ReceivePort for incoming messages
ReceivePort port = ReceivePort();
// Notify any other isolates what port this isolate listen to.
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(Uri.parse(dataURL));
// Lots of JSON to parse
replyTo.send(jsonDecode(respnse.bogy));
}
}
Future sendReceive(SendPort port, msg) {
ReceivePort response = ReceivePort();
port.send([msg, response.sendPort]);
return response.first;
}
在这里,dataLoader()
是Isolate
在其自己的独立执行线程中运行的。在isolate中,您可以执行更多CPU密集型处理(例如解析大的JSON),或执行计算密集型数学运算,例如加密或信号处理。
下面完整例子:
import 'dart:convert';
import 'dart:isolate';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({Key? key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
@override
void initState() {
super.initState();
loadData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: getBody(),
);
}
Widget getBody() {
bool showLoadingDialog = widgets.isEmpty;
if (showLoadingDialog) {
return getProgressDialog();
} else {
return getListView();
}
}
Widget getProgressDialog() {
return const Center(child: CircularProgressIndicator());
}
Widget getListView() {
return ListView.builder(
itemCount: widgets.length,
itemBuilder: (context, position) {
return getRow(position);
},
);
}
Widget getRow(int i) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Text('Row ${widgets[i]['title']}'),
);
}
Future<void> loadData() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
// The 'echo' isolate send its SendPort as the first message.
SendPort sendPort = await receivePort.first;
List msg = await sendReceive(
sendPort, 'https://jsonplaceholder.typicode.com/posts');
setState(() {
widgets = msg;
});
}
// The entry point for the isolate
static Future<void> dataLoader(SendPort sendPort) async {
// Open the ReceivePort for incoming message.
ReceivePort port = ReceivePort();
// Notify any other isolates what port this isolate listens to
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(Uri.parse(dataURL));
// Lots of JSON to parse
replyTo.send(jsonDecode(response.body));
}
}
Future sendReceive(SendPort port, msg) {
ReceivePort response = ReceivePort();
port.send([msg, response.sendPort]);
return response.first;
}
}
image.png
Flutter上的OkHttp相当于什么?
http
当您使用流行的包时,在 Flutter 中进行网络调用很容易。
虽然http包不具备Okhttp中的所有功能,但它抽象出了您通常自己实现的大部分网络,使其成为进行网络调用的简单方法。
要使用该http
包,请将其添加到您的以来项目中pubspec.yaml
dependencies:
...
http: ^0.11.3+16
要进行网络请求,请调用await/async
函数http.get()
import 'dart:developer' as developer;
import 'package:http/http.dart' as http;
Future<void> loadData() async {
var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
http.Response response = awaitt http.get(dataURL);
developer.log(response.body);
}
如何显示长时间运行任务的进度
在Android中,您通常会ProgressBar
在后台线程上执行长时间运行的任务时在UI中显示一个视图。
在Flutter中,使用ProgressIndicator
小部件。通过控制何时通过布尔标志呈现进度,以编程方式显示进度。告诉Flutter在长时间运行的任务开始之前更新他的状态,并在它结束后隐藏它。
在下面的示例中,构建函数被分成三个不同的函数。如果showLoadingDialog
是true
(当widgets.isEmpty
),则渲染ProgressIndicator‘
。否则通过网络返回的数据显示到ListView
。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({Key? key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
@override
void initState() {
super.initState();
loadData();
}
Future<void> loadData() async {
var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
http.Response response = await http.get(dataURL);
setState(() {
widgets = jsonDecode(response.body);
});
}
Widget getBody() {
bool showLoadingDialog = widgets.isEmpty;
if (showLoadingDialog) {
return getProgressDialog();
} else {
return getListView();
}
}
Widget getProgressDialog() {
return const Center(child: CircularProgressIndicator());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: getBody(),
);
}
ListView getListView() {
return ListView.builder(
itemCount: widgets.length,
itemBuilder: (context, position) {
return getRow(position);
});
}
Widget getRow(int i) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Text("Row ${widgets[i]["title"]}"),
);
}
}
image.png
本文转自 https://juejin.cn/post/7182563823210430525,如有侵权,请联系删除。
网友评论