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视频教程-小部件
⑩、Dart单线程-事件循环-Future篇
前言 - 线程、阻塞、事件循环
一、先了解几个概念
- 多线程
- 单线程
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
官方说明
- Future 表示异步操作的结果,可以有两种状态:未完成或已完成。
如果异步操作成功,future 以一个值结束。否则,它会以错误完成。- 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
异步函数同时使用
- await 必须在 async(异步函数)函数中才能使用
- 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.使用 await
和 async
进行多次请求
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 是多线程的
包含
- UI Runner
- GPU Runner
- IO Runner
- 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}