声明:阅读该文章请确保你有 Android 开发的相关知识
这是《Android 开发者的 Flutter》系列的第六篇,如果想看上一篇请参考:
Android 开发者的 Flutter(五) —— Flutter 中的 Intent 及处理

Flutter 中的 runOnUiThread()

Dart 有一个单线程执行模型,支持 Isolates 事件循环和异步编程(在另一个线程上运行 Dart 代码)。除非你生成一个 Isolate,否则你的 Dart 代码始终是在 UI 线程中运行,并由事件循环驱动。Flutter 的事件循环相当于 Android 的 main Looper—— 也就是说,Looper 是在主线程上执行。

Dart 的线程模型并不强制您将所有阻塞线程的操作都放入子线程中运行,但是那样就会导致 UI 线程冻结。不同于 Android 要求您始终保持主线程空闲,在Flutter中,您只需使用 Dart 语言提供的异步工具(如 async/ await)来执行异步工作。如果您已经在 C#,Javascript 中使用过它,或者您已经使用过 Kotlin 的协同程序,那么您可能熟悉 async/ await 范例。

例如,您可以通过使用 async/ await 运行网络代码而不会导致 UI 阻塞,让 Dart 完成繁重的工作:

loadData() async {  String dataURL = "https://jsonplaceholder.typicode.com/posts";  http.Response response = await http.get(dataURL);  setState(() {    widgets = json.decode(response.body);  });}

一旦 await 网络调用完成,您将更新 UI 调用 setState(),这会触发重构构件树并更新数据。

接下来,这里是一个异步加载数据的例子,并将其显示在 ListView 中:

import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(new SampleApp());}class SampleApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return new MaterialApp(      title: 'Sample App',      theme: new ThemeData(        primarySwatch: Colors.blue,      ),      home: new SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  SampleAppPage({Key key}) : super(key: key);  @override  _SampleAppPageState createState() => new _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List widgets = [];  @override  void initState() {    super.initState();    loadData();  }  @override  Widget build(BuildContext context) {    return new Scaffold(      appBar: new AppBar(        title: new Text("Sample App"),      ),      body: new ListView.builder(          itemCount: widgets.length,          itemBuilder: (BuildContext context, int position) {            return getRow(position);          }));  }  Widget getRow(int i) {    return new Padding(      padding: new EdgeInsets.all(10.0),      child: new Text("Row ${widgets[i]["title"]}")    );  }  loadData() async {    String dataURL = "https://jsonplaceholder.typicode.com/posts";    http.Response response = await http.get(dataURL);    setState(() {      widgets = json.decode(response.body);    });  }}

有关在 Flutter 中进行后台工作的更多信息以及它与 Android 的不同之处,请参阅下一节。

如何将工作移至后台线程

在 Android 中,当您想要访问网络资源时,通常会转到后台线程并执行工作,以免阻塞主线程并避免 ANR。例如,您可能正在使用的 AsyncTask,一个 LiveData,一个 IntentService,一个 JobScheduler 工作,或与在后台线程工作调度的 RxJava。

由于 Flutter 是单线程的并且运行一个事件循环(如 Node.js),因此您不必担心线程管理或后台线程后缀。如果您正在执行 I / O 绑定工作,例如磁盘访问或网络调用,那么您可以安全地使用 async/ await 来运行准备好的代码。另一方面,如果您需要进行计算密集型工作以保持 CPU 繁忙,则您需要将其移至某个本地 Isolate,以避免阻塞事件循环,就像您在 Android 中希望将任何类型的工作保留在主线程之外。

对于 I / O 绑定的工作,您可以将函数声明为一个 async 函数,并 await 在函数中长时间运行任务:

loadData() async {  String dataURL = "https://jsonplaceholder.typicode.com/posts";  http.Response response = await http.get(dataURL);  setState(() {    widgets = json.decode(response.body);  });}

经常使用的网络或数据库调用这两种都是 I / O 操作。

在 Android 上,当您继承 AsyncTask 时,通常会重写 3 个方法 onPreExecute(),doInBackground() 和 onPostExecute()。Flutter 中没有类似的方案,因为你只是等待一个长时间运行的函数,而 Dart 的事件循环会处理这些事情。

但是,有时您可能正在处理大量数据,并且您的 UI 可能会挂起。在这种情况下,就像在 Android 上一样,在 Flutter 中,可以利用多个 CPU 内核来执行长时间运行或计算密集型任务。这是通过使用 Isolates 完成的。

Isolates 是一个独立的执行线程,它运行时不会与主执行内存堆共享任何内存。这意味着你不能从主线程访问变量或通过调用来更新你的 UI setState()。Isolates 对他们的名字是真实的;与 Android 线程不同,Isolates 不能共享内存(例如,以静态字段的形式)。

我们来看一个简单的 Isolates 例子,以及如何将数据传递回主线程以更新UI。

loadData() async {  ReceivePort receivePort = new 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;  });}// The entry point for the isolatestatic dataLoader(SendPort sendPort) async {  // Open the ReceivePort for incoming messages.  ReceivePort port = new 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(dataURL);    // Lots of JSON to parse    replyTo.send(json.decode(response.body));  }}Future sendReceive(SendPort port, msg) {  ReceivePort response = new ReceivePort();  port.send([msg, response.sendPort]);  return response.first;}

在这里,dataLoader() 是 Isolate 在它自己的独立执行线程中运行的。在此 Isolates 中,您可以执行更多的 CPU 密集型处理,例如解析大型 JSON,或执行计算密集型数学(如密码或信号处理)。

下面是一个可以运行的完整示例。

import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;import 'dart:async';import 'dart:isolate';void main() {  runApp(new SampleApp());}class SampleApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return new MaterialApp(      title: 'Sample App',      theme: new ThemeData(        primarySwatch: Colors.blue,      ),      home: new SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  SampleAppPage({Key key}) : super(key: key);  @override  _SampleAppPageState createState() => new _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List widgets = [];  @override  void initState() {    super.initState();    loadData();  }  showLoadingDialog() {    if (widgets.length == 0) {      return true;    }    return false;  }  getBody() {    if (showLoadingDialog()) {      return getProgressDialog();    } else {      return getListView();    }  }  getProgressDialog() {    return new Center(child: new CircularProgressIndicator());  }  @override  Widget build(BuildContext context) {    return new Scaffold(        appBar: new AppBar(          title: new Text("Sample App"),        ),        body: getBody());  }  ListView getListView() => new ListView.builder(      itemCount: widgets.length,      itemBuilder: (BuildContext context, int position) {        return getRow(position);      });  Widget getRow(int i) {    return new Padding(padding: new EdgeInsets.all(10.0),                        child: new Text("Row ${widgets[i]["title"]}"));  }  loadData() async {    ReceivePort receivePort = new 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;    });  }// the entry point for the isolate  static dataLoader(SendPort sendPort) async {    // Open the ReceivePort for incoming messages.    ReceivePort port = new 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(dataURL);      // Lots of JSON to parse      replyTo.send(json.decode(response.body));    }  }  Future sendReceive(SendPort port, msg) {    ReceivePort response = new ReceivePort();    port.send([msg, response.sendPort]);    return response.first;  }}

Flutter 中的 OkHttp

在使用流行的 http 软件包时,使用 Flutter 进行网络通信非常简单。

虽然 http 包没有 OkHttp 已经实现的所有功能,但它可以抽象出很多通常自己实现的网络,使其成为进行网络调用的一种简单方法。

您可以通过将其添加到您的依赖关系中来使用它 pubspec.yaml:

dependencies:  ...  http: '>=0.11.3+16'

然后,进行网络通信,您只需 await 中执行以下 async 功能 http.get():

import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;[...]  loadData() async {    String dataURL = "https://jsonplaceholder.typicode.com/posts";    http.Response response = await http.get(dataURL);    setState(() {      widgets = json.decode(response.body);    });  }}

如何在 Flutter 中显示长时间任务的进度

在 Android 中,ProgressBar 当您在后台线程上执行长时间运行的任务时,您通常会在 UI 中显示视图。

在 Flutter 中,这可以通过使用 ProgressIndicator 小部件来完成。您可以通过编程方式显示进度 UI,方法是通过布尔型标志在控制呈现到进程 UI,并告诉 Flutter 在长时间运行任务开始之前更新其状态,并在结束之后隐藏它。

在下面的例子中,我们将构造函数分解为三个不同的函数。如果 showLoadingDialog() 是 true(当 widgets.length == 0)然后我们渲染 ProgressIndicator,否则我们在 ListView 中用数据显示。

import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(new SampleApp());}class SampleApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return new MaterialApp(      title: 'Sample App',      theme: new ThemeData(        primarySwatch: Colors.blue,      ),      home: new SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  SampleAppPage({Key key}) : super(key: key);  @override  _SampleAppPageState createState() => new _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List widgets = [];  @override  void initState() {    super.initState();    loadData();  }  showLoadingDialog() {    return widgets.length == 0;  }  getBody() {    if (showLoadingDialog()) {      return getProgressDialog();    } else {      return getListView();    }  }  getProgressDialog() {    return new Center(child: new CircularProgressIndicator());  }  @override  Widget build(BuildContext context) {    return new Scaffold(        appBar: new AppBar(          title: new Text("Sample App"),        ),        body: getBody());  }  ListView getListView() => new ListView.builder(      itemCount: widgets.length,      itemBuilder: (BuildContext context, int position) {        return getRow(position);      });  Widget getRow(int i) {    return new Padding(padding: new EdgeInsets.all(10.0),                        child: new Text("Row ${widgets[i]["title"]}"));  }  loadData() async {    String dataURL = "https://jsonplaceholder.typicode.com/posts";    http.Response response = await http.get(dataURL);    setState(() {      widgets = json.decode(response.body);    });  }}

更多相关文章

  1. android进行异步更新UI的四种方式
  2. android Handler,Looper,Message三者关系
  3. Android(安卓)性能典范:拯救计划
  4. SurfaceView
  5. Android开发: 线程间消息通信 Looper 和Handler
  6. android的surfaceview的用法
  7. Android面试知识点总结-Android篇
  8. Android中生成和扫描二维码
  9. Android(安卓)Handler 异步消息处理机制的妙用 创建强大的图片加

随机推荐

  1. Android N 指纹框架
  2. android 系统中静音后使得音量减键不能解
  3. android 布局式跑马灯,非TextView
  4. Android TabHost使用、动态加载内容
  5. Android开发之消息处理机制(一)——Handler
  6. Android异步加载图像小结 (含线程池,缓存方
  7. android style
  8. 五幅图学会Android(安卓)Canvas的坐标系
  9. Android,一个思路实现APP版本更新
  10. Android 任务和回退堆栈---启动任务