在Android开发中,异步编程是必不可少的,比如网络请求、IO操作等很多都是异步操作,而在Android原生中,有主线程和工作线程的概念,耗时操作都是要放到工作线程中的,ui要在主线程中更新,因此,原生Android开发中对线程的处理是必不可少的,幸运的是,一些第三方库例如Rxjava、RxAndroid让我们的线程切换起来十分的方便。
但是Flutter是基于Datt语言实现的,而Dart中的代码是在一个线程中运行的,因此,Flutter也是单线程模型的。
参考资料可以看官方说明:Dart异步编程
这里不禁有一个问题,Flutter为啥要用Dart呢?单线程会不会比多线程的效率低呢?
先不管效率的问题了,实际运行起来速度还是很快的。
也就是说,我们在Flutter中对异步的处理并不是像原生Android那样是多个线程去处理的。
那么,Flutter中是怎么处理异步操作的呢?
Flutter给我们提供了Future对象以及async和await关键字来支持异步编程。
我们首先来看看 Future
Future对象表示异步操作的结果,我们通常通过then()来处理返回的结果
async用于标明函数是一个异步函数,其返回值类型是Future类型
await用来等待耗时操作的返回结果,这个操作会阻塞到后面的代码
网络请求是非常典型的异步任务,下面我们就来结合网络请求来看看Flutter中的异步是如何使用的。
网络请求的方式有很多,这里我就直接用目前比较好用的DIO网络请求库 了,你也可以使用官方文档中的网络请求 ,都是可以的。
下面我们来简单用一用网络请求。
这里我使用的聚合上的一个接口
接口地址:http://v.juhe.cn/toutiao/index?type=keji&key=4c52313fc9247e5b4176aed5ddd56ad7
关于DIO如何使用这里就不讲了,Github上文档很详细,使用起来也很简单。
下面我们直接用:
首先要先导包
import 'package:dio/dio.dart';
请求接口获取数据的方法
/**
* 请求接口获取数据
*/
Future getData() async {
String url = "http://v.juhe.cn/toutiao/index";
String key = "4c52313fc9247e5b4176aed5ddd56ad7";
String type = "keji";
print("开始请求数据");
Response response =
await Dio().get(url, queryParameters: {"type": type, "key": key});
print("请求完成");
return response;
}
注意一下几点:
然后我们就可以在main函数中来接收网络请求后的结果了:
main() {
getData().then((result) {
print("接口返回的数据是:${result}");
}).whenComplete((){
print("异步任务处理完成");
}).catchError((){
print("出现异常了");
});
print("我是在请求数据后面的代码呦!");
}
这样一来,我们就完成了Flutter中的异步操作了,可以看到,相对于原生Android来讲,Flutter中的异步是非常简单的。
FutureBuilder 实际上就是对Future进行封装的一个Widget。我们先来看看他的构造方法
const FutureBuilder({
Key key,
this.future,
this.initialData,
@required this.builder
})
其中,
future接收Future
类型的值,实际上就是我们的异步函数,通常情况下都是网络请求函数
initialData 表示在异步函数执行完成之前可以给快照进行使用,简单理解就是初始数据,应该不是很常用
builder:接收一个AsyncWidgetBuilder
类型的值,看源码
/// Signature for strategies that build widgets based on asynchronous
/// interaction.
///
/// See also:
///
/// * [StreamBuilder], which delegates to an [AsyncWidgetBuilder] to build
/// itself based on a snapshot from interacting with a [Stream].
/// * [FutureBuilder], which delegates to an [AsyncWidgetBuilder] to build
/// itself based on a snapshot from interacting with a [Future].
typedef AsyncWidgetBuilder = Widget Function(BuildContext context, AsyncSnapshot snapshot);
AsyncWidgetBuilder为构建器提供了一个AsyncSnapshot对象,我们再来看看AsyncSnapshot的源码
AsyncSnapshot中封装了connectionState(连接状态)、data(实际上就是future执行后返回的数据)以及error(实际上就是future错误时返回的错误信息)
data和error比较好理解,我们主要来看看connectionState
connectionState是一个enum 类型的值,其源码如下
enum ConnectionState {
/// Not currently connected to any asynchronous computation.
///
/// For example, a [FutureBuilder] whose [FutureBuilder.future] is null.
none,
/// Connected to an asynchronous computation and awaiting interaction.
waiting,
/// Connected to an active asynchronous computation.
///
/// For example, a [Stream] that has returned at least one value, but is not
/// yet done.
active,
/// Connected to a terminated asynchronous computation.
done,
}
搞清楚这些,我们就可以开心的使用FutureBuilder了。
一个很常见的需求,在首次进入页面时,此时数据还需要从网络上获取,我们希望在网络请求完成之前显示一个加载页面,请求完成之后再显示数据。此时,我们就可以使用FutureBuilder来完成了
首先是接口请求函数,为了更明显的能看到加载控件的显示,这里的异步请求函数中我延时3秒后再请求数据,代码如下
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_async/widget/loading.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '新闻列表',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: '新闻列表'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: _getNews(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
/*表示数据成功返回*/
if (snapshot.hasData) {
Response response = snapshot.data;
return Text("${response.data.toString()}");
} else {
return LoadingWidget();
}
},
));
}
}
/**
* 请求接口获取数据
*/
Future _getNews() async {
await Future.delayed(Duration(seconds: 3), () {
print("延时三秒后请求数据");
});
String url = "http://v.juhe.cn/toutiao/index";
String key = "4c52313fc9247e5b4176aed5ddd56ad7";
String type = "keji";
print("开始请求数据");
Response response =
await Dio().get(url, queryParameters: {"type": type, "key": key});
print("请求完成");
return response;
}
下面是demo,需要的可以下载:
flutter_async Demo
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可。也可以关注我的 Flutter 博客专栏,我会不定期的更新,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!