9.线程和异步UI
声明:Flutter专栏文档均来自慕课网 https://coding.imooc.com/class/321.html
线程和异步UI
怎么编写异步的代码?
怎么把工作放到后台线程执行?
如何进行网络请求?
如何为长时间运行的任务添加一个进度指示器?
怎么编写异步的代码?
Dart有一个单线程执行模型,支持Isolate(一种在另一个线程上运行Dart代码的方法),一个事件循环和异步编程。除非你自己创建一个 Isolate ,否则你的 Dart 代码永远运行在主UI 线程,并由 event loop 驱动。Flutter 的 event loop 和 iOS 中的 main loop 相似:Looper 是附加在主线程上的。
Dart 的单线程模型,并不意味着你写的代码一定要作为阻塞操作的方式运行,从而卡住 UI。相反,可以使用 Dart 语言提供的异步工具,例如async / await,来实现异步操作。
举个例子,你可以使用async / await来让 Dart 帮你做一些繁重的工作,编写网络请求代码而不会挂起 UI:
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}上面做法等价于Android中的
runOnUiThread。
一旦await 的网络请求完成,通过调用setState()来更新 UI,这会触发 widget 子树的重建,并更新相关数据。
下面的例子展示了异步加载数据,并用ListView展示出来:
怎么把工作放到后台线程执行?
由于 Flutter 是单线程并且跑着一个 event loop(就像 Node.js),因此你不必担心线程管理或生成后台线程。如果你正在做 I/O 操作,如访问磁盘或网络请求,可以安全地使用async / await来完成。如果你需要做让 CPU 执行繁忙的计算密集型任务,你需要使用 Isolate 来避免阻塞 event loop。
在Android中,当你想访问一个网络资源时,你通常会创建一个AsyncTask,当你需要一个耗时的后台任务时,你通常需要IntentService,在Flutter中则不需要这么繁琐。
对于 I/O 操作,通过关键字 async把方法声明为异步方法,然后通过await关键字等待该异步方法执行完成:
在Android中,当你继承AsyncTask时,通常会覆盖3个方法,OnPreExecute、doInBackground和onPostExecute。 在Flutter中没有这种模式的等价物,因为你只需await函数执行完成,而Dart的事件循环将负责其余的事情。
以上就是对诸如网络请求、数据库访问等,I/O 操作的典型做法。
然而,有时候你需要处理大量的数据,这会导致你的 UI 挂起。在 Flutter 中,使用 Isolate 来发挥多核心 CPU 的优势来处理那些长期运行或是计算密集型的任务。
Isolate 是分离的运行线程,并且不和主线程的内存堆共享内存。这意味着你不能访问主线程中的变量,或者使用 setState() 来更新 UI。正如它们的名字一样,Isolate不能共享内存。
下面的例子展示了一个简单的Isolate是如何把数据返回给主线程来更新 UI 的:
这里,dataLoader() 是一个运行于自己独立执行线程上的 Isolate。在 Isolate 里,你可以执行 CPU 密集型任务(例如解析一个庞大的 json,解析json也是很耗时的哦),或是计算密集型的数学操作,如加密或信号处理等。
你可以运行下面的完整例子:
如何进行网络请求?
在 Flutter 中,使用流行的! http package做网络请求非常简单。它把你可能需要自己做的网络请求操作抽象了出来,让发起请求变得简单。
要使用 http 包,在pubspec.yaml 中添加如下依赖:
发起网络请求,在http.get() 这个 async 方法中使用 await :
一旦获得结果后,你可以通过调用setState来告诉Flutter更新其状态,setState将使用网络调用的结果更新UI。
关于网络请求的更多内容和实战技巧可学习《基于Http实现网络操作》部分的课程。
如何为长时间运行的任务添加一个进度指示器?
在 iOS 中,在后台运行耗时任务时我们通常会使用 UIProgressView。
在 Android 中,在后台运行耗时任务时我们通常会使用 ProgressBar。
那么,在Flutter也有与之对应的widget叫ProgressIndicator。通过一个布尔 flag 来控制是否展示进度。在任务开始时,告诉 Flutter更新状态,并在结束后隐藏。
在下面的例子中,build函数被拆分成三个函数。如果showLoadingDialog() 是 true (当 widgets.length == 0时),则渲染 ProgressIndicator。否则,当数据从网络请求中返回时,渲染 ListView:
Last updated
Was this helpful?