Flutter中的異步(Future、async、await、FutureBuilder)和 網絡請求

Flutter 異步編程(Future、async、await)

在Android開發中,異步編程是必不可少的,比如網絡請求、IO操作等很多都是異步操作,而在Android原生中,有主線程和工作線程的概念,耗時操作都是要放到工作線程中的,ui要在主線程中更新,因此,原生Android開發中對線程的處理是必不可少的,幸運的是,一些第三方庫例如Rxjava、RxAndroid讓我們的線程切換起來十分的方便。

但是Flutter是基於Datt語言實現的,而Dart中的代碼是在一個線程中運行的,因此,Flutter也是單線程模型的。
參考資料可以看官方說明:Dart異步編程
這裏不禁有一個問題,Flutter爲啥要用Dart呢?單線程會不會比多線程的效率低呢?
在這裏插入圖片描述

先不管效率的問題了,實際運行起來速度還是很快的。

也就是說,我們在Flutter中對異步的處理並不是像原生Android那樣是多個線程去處理的。
那麼,Flutter中是怎麼處理異步操作的呢?

Flutter給我們提供了Future對象以及asyncawait關鍵字來支持異步編程。
我們首先來看看 Future

Future是一個抽象類,我們常用的方法如下
在這裏插入圖片描述

Future對象表示異步操作的結果,我們通常通過then()來處理返回的結果
async用於標明函數是一個異步函數,其返回值類型是Future類型
await用來等待耗時操作的返回結果,這個操作會阻塞到後面的代碼


Fluture中的網絡請求

網絡請求是非常典型的異步任務,下面我們就來結合網絡請求來看看Flutter中的異步是如何使用的。

網絡請求的方式有很多,這裏我就直接用目前比較好用的DIO網絡請求庫 了,你也可以使用官方文檔中的網絡請求 ,都是可以的。

下面我們來簡單用一用網絡請求。

這裏我使用的聚合上的一個接口

接口地址:http://v.juhe.cn/toutiao/index?type=keji&key=4c52313fc9247e5b4176aed5ddd56ad7

關於DIO如何使用這裏就不講了,Github上文檔很詳細,使用起來也很簡單。
下面我們直接用:

首先要先導包

import 'package:dio/dio.dart';

請求接口獲取數據的方法

/**
 * 請求接口獲取數據
 */
Future<Response> 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中的異步操作了,可以看到,相對於原生Android來講,Flutter中的異步是非常簡單的。


Flutter FutureBuilder

FutureBuilder 實際上就是對Future進行封裝的一個Widget。我們先來看看他的構造方法

  const FutureBuilder({
    Key key,
    this.future,  
    this.initialData,
    @required this.builder
  })

其中,
future接收Future<T>類型的值,實際上就是我們的異步函數,通常情況下都是網絡請求函數
initialData 表示在異步函數執行完成之前可以給快照進行使用,簡單理解就是初始數據,應該不是很常用
builder:接收一個AsyncWidgetBuilder<T>類型的值,看源碼

/// 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<T> = Widget Function(BuildContext context, AsyncSnapshot<T> snapshot);

AsyncWidgetBuilder爲構建器提供了一個AsyncSnapshot對象,我們再來看看AsyncSnapshot的源碼
在這裏插入圖片描述

下圖是AsyncSnapshot中的屬性和方法
在這裏插入圖片描述

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<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: FutureBuilder(
          future: _getNews(),
          builder: (BuildContext context, AsyncSnapshot<Response> snapshot) {
            /*表示數據成功返回*/
            if (snapshot.hasData) {
              Response response = snapshot.data;
              return Text("${response.data.toString()}");
            } else {
              return LoadingWidget();
            }
          },
        ));
  }
}


/**
 * 請求接口獲取數據
 */
Future<Response> _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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章