Flutter中的异步(Future、async、await、FutureBuilder)和 网络请求

Flutter 异步编程(Future、async、await)

在Android开发中,异步编程是必不可少的,比如网络请求、IO操作等很多都是异步操作,而在Android原生中,有主线程和工作线程的概念,耗时操作都是要放到工作线程中的,ui要在主线程中更新,因此,原生Android开发中对线程的处理是必不可少的,幸运的是,一些第三方库例如Rxjava、RxAndroid让我们的线程切换起来十分的方便。

但是Flutter是基于Datt语言实现的,而Dart中的代码是在一个线程中运行的,因此,Flutter也是单线程模型的。
参考资料可以看官方说明:Dart异步编程
这里不禁有一个问题,Flutter为啥要用Dart呢?单线程会不会比多线程的效率低呢?
Flutter中的异步(Future、async、await、FutureBuilder)和 网络请求_第1张图片

先不管效率的问题了,实际运行起来速度还是很快的。

也就是说,我们在Flutter中对异步的处理并不是像原生Android那样是多个线程去处理的。
那么,Flutter中是怎么处理异步操作的呢?

Flutter给我们提供了Future对象以及asyncawait关键字来支持异步编程。
我们首先来看看 Future

Future是一个抽象类,我们常用的方法如下
Flutter中的异步(Future、async、await、FutureBuilder)和 网络请求_第2张图片

Future对象表示异步操作的结果,我们通常通过then()来处理返回的结果
async用于标明函数是一个异步函数,其返回值类型是Future类型
await用来等待耗时操作的返回结果,这个操作会阻塞到后面的代码


Flutter中的网络请求

网络请求是非常典型的异步任务,下面我们就来结合网络请求来看看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;
}

注意一下几点:

  1. 网络请求是耗时操作
  2. 要使用async来标明getData这个函数是一个异步函数
  3. await 用于等待请求返回的结果,此时会阻塞掉后面的代码,只有当请求结束后面的代码才会执行
  4. async标注的函数其返回值类型是Future

然后我们就可以在main函数中来接收网络请求后的结果了:

main() {
  getData().then((result) {
    print("接口返回的数据是:${result}");
  }).whenComplete((){
    print("异步任务处理完成");
  }).catchError((){
    print("出现异常了");
  });

  print("我是在请求数据后面的代码呦!");
}


我们来看看执行的结果:Flutter中的异步(Future、async、await、FutureBuilder)和 网络请求_第3张图片

这样一来,我们就完成了Flutter中的异步操作了,可以看到,相对于原生Android来讲,Flutter中的异步是非常简单的。


Flutter FutureBuilder

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的源码
Flutter中的异步(Future、async、await、FutureBuilder)和 网络请求_第4张图片

下图是AsyncSnapshot中的属性和方法
Flutter中的异步(Future、async、await、FutureBuilder)和 网络请求_第5张图片

AsyncSnapshot中封装了connectionState(连接状态)data(实际上就是future执行后返回的数据)以及error(实际上就是future错误时返回的错误信息)

dataerror比较好理解,我们主要来看看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,
}

  • none :当前未连接到任何异步计算。
  • waiting : 连接成功等待交互
  • active :正在交互中,可以理解为正在返回数据
  • done :交互完成,可以理解为数据返回完成,此时如果是正确的返回则data就有数据了

搞清楚这些,我们就可以开心的使用FutureBuilder了。


Flutter 请求网络数据时显示加载中

一个很常见的需求,在首次进入页面时,此时数据还需要从网络上获取,我们希望在网络请求完成之前显示一个加载页面,请求完成之后再显示数据。此时,我们就可以使用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;
}


运行效果:
Flutter中的异步(Future、async、await、FutureBuilder)和 网络请求_第6张图片

下面是demo,需要的可以下载:
flutter_async Demo


如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可。也可以关注我的 Flutter 博客专栏,我会不定期的更新,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

你可能感兴趣的:(Flutter)