【Flutter从入门到实战】⑩、Dart的Future和网络、Future-await-async、多核CPU-Isolate、网络请求、Dio的使用-二次封装

Flutter从入门到实战
一共分为23个系列
①(Flutter、Dart环境搭建篇) 共3个内容 已更新
②(Dart语法1 篇) 共4个内容 已更新
③(Dart语法2 篇) 共2个内容 已更新
④(Flutter案例开发篇) 共4个内容 已更新
⑤(Flutter的StatelessWidget 共3个内容 已更新
⑥(Flutter的基础Widget篇) 共2个内容 已更新
⑦(布局Widget篇) 共1个内容 已更新
⑧(Flex、Row、Column以及Flexible、Stack篇) 共1个内容 已更新
⑨(滚动的Widget篇) 共4个内容 已更新
⑩(Dart的Future和网络篇) 共3个内容 已更新
⑪(豆瓣案例-1篇) 共3个内容 已更新
⑫(豆瓣案例-2篇) 共3个内容 已更新

官方文档说明

官方视频教程
Flutter的YouTube视频教程-小部件

请添加图片描述


前言 - 线程、阻塞、事件循环

一、先了解几个概念

  1. 多线程
  2. 单线程
    Dart 是单线程 + 事件循环的
    二、阻塞和非阻塞
    阻塞: 但你做一件事情,比如吃饭,如果是阻塞状态 那么就是只能单单吃饭不做任何事情
    非阻塞: 比如吃饭的时候 ,可以看电影 。看电影和吃饭是同步进行的
    三、事件循环
    单线程模型主要是维护着一个事件循环。
    事件循环它是将一系列队列放到事件队列。比如(点击事件、IO事件、网络事件)

①、Dart的异步操作

Dart的异步操作官方文档
Dart的异步操作主要使用Future以及async、await。

阻塞的案例

发送一个网络请求 返回结果 需要耗时5秒 如果不使用Future的情况下

void main(List<String> args) {

  // 1. 阻塞主线程
  print("main start");
  // 发送一个网络请求
  var result = getNetWorkData();
  print(result);
  print("main end");
}
// 模拟一个网络请求
String getNetWorkData() {
  sleep(Duration(seconds: 2));
  return "网络请求返回的数据";
}
// 打印结果
main start
main end
(等待了2)
网络请求返回的数据

1.Future

官方说明
请添加图片描述

  1. Future 表示异步操作的结果,可以有两种状态:未完成或已完成。
    如果异步操作成功,future 以一个值结束。否则,它会以错误完成。
  2. Future的案例 - 网络请求耗时操作
    定义一个返回Future的函数。函数用于做网络请求操作
    网络请求有一定的耗时操作
    那么 怎么才能不阻塞。就是耗时操作用Future包裹进行返回。
    Future的调用流程。
    Future函数没有抛出异常时 会调用then
    Future函数有抛出异常时 会调用catchError
1.1 Future 模拟一个异步请求操作 得到返回值
void main(List<String> args) {
  // 2. Future 网洛洛请求
  print("main start");
  // 发送一个网络请求
  var future = FutureGetNetWorkData();
  print(future); // Instance of 'Future<dynamic>'
  // 获取真正的值
  // 2.拿到结果(dynamic)
  // then 后面的回调函数什么时候被执行?
  //  需要在 Future(函数)有结果,才执行下面的回调函数
  future.then((value) {
    print(value);
  }).catchError((err) {
    print("err $err");
  }).whenComplete(() {
    print("代码执行完成");
  });
  print("main end");
}
1.2 Future 模拟一个异步请求操作 抛出异常 throw Exception

抛出异常 使用 throw Exception


void main(List<String> args) {

  // 2. Future 网洛洛请求
  print("main start");
  // 发送一个网络请求
  var future = FutureGetNetWorkData();
  print(future); // Instance of 'Future<dynamic>'
  future.then((value) {
    print(value);
  }).catchError((err) {
    print("err $err");
  }).whenComplete(() {
    print("代码执行完成");
  });
  print("main end");
}

// ---
Future FutureGetNetWorkData() {
  // return "网络请求返回的数据";
  return Future(() {
    // 1.  将耗时操作包裹到Future的回调函数中
    // 1. 只要有返回结果 那么就Future对应的then的回调
    // 2. 如果没有结果返回(有错误信息),需要在Future回调中抛出一个异常
    sleep(Duration(seconds: 2));
    throw Exception("我是错误信息");
    // return "hello world";
  });
}

2. Future的链式调用

发送多个请求 可以使用链式操作

void main(List<String> args) {
  Future(() {
    print("main start");
    // // 发送一个网络请求
    // var result = getNetWorkData();
    // print(result);
    // 1. 发送第一次请求
    sleep(Duration(seconds: 3));
    // return "第一次的结果";
    // throw Exception("第一次异常");
  }).then((res) {
    print(res);
    //
    sleep(Duration(seconds: 1));
    // return "第二次的结果";
    throw Exception("第二次异常");
  }).then((res) {
    print(res);
    //
    sleep(Duration(seconds: 1));
    return "第三次的结果";
  }).then((res) {
    print("最终结果$res");
  });

  print("main end");
}

3.Future其他API的使用

3.1 正常的Future 以匿名函数返回

  Future(() {
    return "网络请求";
  }).then((res) {
    print(res);
  });

3.2 直接以字符串形式返回的Future

  Future.value("等同于上面的写法").then((res) {
    print(res);
  });

3.3 抛出一个异常的Future

  // 错误信息
  Future.value("错误信息").catchError((err) {
    print(err);
  });

3.4 延迟执行的Future

 // 延迟执行
  Future<String>.delayed(Duration(seconds: 2), () {
    return "得到结果之前调用";
  }).then((res) {
    print(res);
    return "延迟执行网络请求";
  }).then((res) {
    print(res);
  });

4. await 等待 必须 和async 异步函数同时使用

  1. await 必须在 async(异步函数)函数中才能使用
  2. async 函数返回的结果必须是 Future
/**
 * 1. await 必须在 async(异步函数)函数中才能使用
 * 2.  async 函数返回的结果必须是  Future
*/
Future awaitGetNetwork() async {
  await Future.delayed(Duration(seconds: 3));
  // await sleep(Duration(seconds: 3));
  return "网络请求"; 
    // Future.value("网络请求"); // 语法糖 《等价》 Future.value("网络请求"); // 语法糖

}

// 使用 
// await
  var result = awaitGetNetwork().then((res) {
    print(res);
  });

5.使用 awaitasync 进行多次请求

import 'dart:io';

void main(List<String> args) {
  print("start");
  getData();
  print("end");
}

Future<void> getData() async {
  // 调用上次网络请求
  // 3次网络请求
  // getNetworkData("arg1").then((res) {
  //   print(res);
  // }).then((res) {
  //     print(res);
  // }).then((res) {
  //     print(res);
  // });

  var res1 = await getNetworkData("arg1");
  print(res1);
  var res2 = await getNetworkData(res1);
  print(res2);
  var res3 = await getNetworkData(res2);
  print(res3);
}

Future getNetworkData(String arg) {
  return Future(() {
    sleep(Duration(seconds: 3));
    return "网络请求 " + arg;
  });
}

②、多核CPU的利用

1.Isolate 隔离 的使用

Isolate 单独通过一个线程去执行的

import 'dart:isolate';

void main(List<String> args) {
  print("main start");
  // 1. 第一个参数是 需要传递的函数
  // 2. 第二个参数是 参数
  // Isolate 单独通过一个线程去执行的 
  Isolate.spawn(calc, 100);
  print("main end");
}

void calc(int count) {
  var total = 0;
  for (var i = 0; i < count; i++) {
    total += i;
  }
  print(total);
}

2.Isolate之间的通信 单向通信

通信需要创建管道 ReceivePort

import 'dart:isolate';

Future<void> main(List<String> args) async {
  print("main start");

// 1. 创建管道
  ReceivePort receivePort = ReceivePort();

// 2.创建Isolate
  Isolate isolate = await Isolate.spawn<SendPort>(foo, receivePort.sendPort);
// 3.监听管道
  receivePort.listen((message) {
    print(message);
    // 销毁 管道 和 Isolate
    receivePort.close();
    isolate.kill();
  });
  print("main end");
}

void foo(SendPort send) {
  return send.send("网络请求");
}

3.Isolate之间的通信 双向通信 要使用Flutter的框架 在Foundation框架里面

Flutter 是多线程的
包含

  1. UI Runner
  2. GPU Runner
  3. IO Runner
  4. Platform Runner
 void runCalc() async {
    // compute 是基于 Isolate
    print("调用了runCalc");
    var result = await compute(calc,100);
    print("-----:$result");
  }
  int calc(int count){
    int total = 0;
    for (int i = 0; i < count;i++){
      total +=i;
    }
    return total;
  }

③、网络请求

Dio第三方库
dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配器等…

1. Dio基本使用

通过pub.dev查找如何安装dio
请添加图片描述
2. 通过 终端 指令 flutter pub get 进行安装
请添加图片描述
3. 导入dio的库
请添加图片描述
4. 建议使用第三方库都进行一次封装。
可以进行参数的拦截器
防止库的作者不维护了。那么引入的库都需要改。

import 'package:flutter/material.dart'; // runApp在这个material库里面
import 'package:dio/dio.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: YHiOSHomePage(),
    );
  }
}

class YHiOSHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Dio网络请求"),
      ),
      body: HYiOSContentState(),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
    );
  }
}

class HYiOSContentState extends StatefulWidget {
  const HYiOSContentState({Key? key}) : super(key: key);

  @override
  State<HYiOSContentState> createState() => _HYiOSContentStateState();
}

class _HYiOSContentStateState extends State<HYiOSContentState> {

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    // 发送网络请求
    // 1.创建Dio对象
    final dio = Dio();

    // 2. 发送网络请求
    // https://httpbin.org/get 是测试网络请求 get 或者post的
    // https://httpbin.org/post 是测试网络请求 get 或者post的

    dio.get("https://httpbin.org/get").then((res){
      print("get $res");
    });

    dio.post("https://httpbin.org/post").then((res){
      print("post $res");
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

1.请求的打印结果


flutter: get {"args":{},"headers":{"Accept-Encoding":"gzip","Host":"httpbin.org","User-Agent":"Dart/2.16 (dart:io)","X-Amzn-Trace-Id":"Root=1-6231fdce-7cd939772da7cc4234a6ac23"},"origin":"47.57.136.111","url":"https://httpbin.org/get"}
flutter: post {"args":{},"data":"","files":{},"form":{},"headers":{"Accept-Encoding":"gzip","Content-Length":"0","Content-Type":"application/json; charset=utf-8","Host":"httpbin.org","User-Agent":"Dart/2.16 (dart:io)","X-Amzn-Trace-Id":"Root=1-6231fdce-0467a9f02657c9d058228b52"},"json":null,"origin":"47.57.136.111","url":"https://httpbin.org/post"}

2.Dio二次封装

1.请求基本配置 url 请求超时时间
config.dart
class HttpConfig{
  static const String baseUrl = "https://httpbin.org";
  static const int timeout = 6000;
}
2.请求类封装 Url 参数 拦截器 异常捕获
http_request.dart
// 封装建议
// 命名最好使用 单词使用下划线分开

import 'package:dio/dio.dart';
import 'package:learn_flutter/service/config.dart';
class HttpRequest {
  // Dio 有基本的配置
  static final BaseOptions baseOptions = BaseOptions(
      baseUrl: HttpConfig.baseUrl,connectTimeout: HttpConfig.timeout
  );
  static final Dio dio = Dio(baseOptions);
  // static void request(String url,
  // 由于异步请求 所以使用 Future<T>
  // 其中T代表泛型 因为不知道服务器返回什么具体的类型 所以使用泛型代表
static Future<T> request<T>(String url,
                      {String method="get",
                        Map<String,dynamic>? params,
                        Interceptor? inter}) async
      {
        // 1. 创建单独配置
        final option = Options(method: method);
        // 全局拦截器
        // 创建默认的全局拦截器
       
        Interceptor dInter = InterceptorsWrapper(
            onRequest: (request,handle) {
              print("请求拦截");
              return handle.next(request);
            },
            onResponse: (response,handle) {
              print("响应拦截");
              return handle.next(response);
            },
            onError: (e, handler) async {
              print("错误拦截");
              return handler.next(e);
            }
        );
        List<Interceptor> inters = [dInter];
        // 请求单独拦截器
        if (inter != null) {
          inters.add(inter);
        }
        // 统一添加到拦截器中
        dio.interceptors.addAll(inters);

        // 2. 发送网络请求 url 参数 配置
        // 请求可能发生错误 所以使用try 捕捉异常
        try{
          Response response = await dio.request(url,queryParameters: params,options: option);
          // 3. 返回响应的数据
          return response.data;

        } on DioError catch(e) {
          // 请求失败情况
          return Future.error(e);
        }



      }
}```
#### 3.Dio二次封装的使用
```js
   HttpRequest.request("https://httpbin.org/get", params: {"name":"宇夜iOS"},inter:InterceptorsWrapper(
        onError: (e,handle)
    {
      print("错误信息 $e");
    },     
    ))
        .then((res){
        print("HttpRequest get $res");
     })
        .catchError( (e) {
      print("错误信息 $e");
      });
Dio二次封装的打印结果
flutter: HttpRequest get {args: {name: 宇夜iOS}, headers: {Accept-Encoding: gzip, Host: httpbin.org, User-Agent: Dart/2.16 (dart:io), X-Amzn-Trace-Id: Root=1-62320863-58fc800073900a263c10431a}, origin: 47.57.136.111, url: https://httpbin.org/get?name=宇夜iOS}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇夜iOS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值