终于有人把 【移动开发】 从基础到实战的全套视频弄全了
一、前言
学习了布局实例和交互后,算是对Flutter
入门了,基本可以实现一些普通页面搭建和交互效果了。但是这远远还不够,现在App
都是需要网络访问的,而今天的目标就是学习IO
和网络这一块。
二、Dart中的异步任务消息循环机制
Dart
是单线程模型,什么是单线程模型呢?单线程就是在程序执行时,所走的程序路径按照连续顺序排列下来,前面的必须处理好,后面的才会执行(就是同一个时刻只能执行一个操作)。生活中举个例子,在早上上班时,需要指纹打卡,正要打卡的时候,突然来电话,这时候你接电话,接完电话再打卡,从接电话到打卡这操作就是单线程,也就是说,在接电话的时候,打卡这个操作是阻塞的,得要接完电话才能打卡。什么是异步?在计算机领域中,异步指的是线程不需要一直等待下去,而是继续执行下面的操作,不管其他线程的状态,当由消息返回时系统会通知线程进行处理。就好像在平时生活中,我现在需要煮饭,我并不是等着饭煮熟才能去做其他事,而是把电饭锅煮饭按钮按下,然后就可以去看电视,看书等等,等饭好了电饭锅的按钮会跳到保温状态,这时候你就可以吃饭了。这时候我也想到了再Android
中OkHttp
的同步和异步请求:
- 同步:向后台发送一个网络请求,等待后台返回数据结果,然后再发送下一个网络请求
- 异步:向后台发送一个网络请求,不需要等待后台返回数据,随时可以发送下一个网络请求
但是Flutter
中的异步有些不一样,下面慢慢讲述。
1.事件循环体系
1.1.Event-Looper
Dart
是单线程模型,并没有主线程/子线程之分,Dart
是Event loops
和Event Queue
模型,而EventLooper
将所有的事件依次执行,直接上图:
上图中很直观的反映了Dart
中的Event
处理模式,当产生一个Event
之后,会进入Event queue
,而Event loop
从EventQueue
中获取Event
并且处理。
1.2.单线程模型
当一个Dart
函数开始执行,那么它就会执行到这个函数结束,也就是函数不会被其他代码所打断。这里首先解释一下什么是Dart
中的isolate
。isolate
本身是隔离的意思,有自己的内存和单线程控制的实体,因为isolate
之间的内存在逻辑是隔离的,isolate
的代码是按顺序执行的。在Dart
中并发可以使用用isolate
,isolate
和Thread
很像,但是isolate
之间没有共享内存。一个Dart
程序是在Main isolate
的Main函数开始,我们平时开发中,默认环境就是Main isolate
,App的启动入口main
函数就是一个isolate
,在Main函数结束后,Main isolate
线程开始一个一个处理Event Queue
中的每一个Event
。
1.3.Dart的消息循环和消息队列
一个Dart
Main isolate只有一个消息循环(Event Looper)和两个消息队列:Event队列和MicroTask队列。
- Event队列包含所有外来事件:I/O,mouse events(鼠标事件),drawing events(绘图),timers(计时器),isolate之间的message等
- microTask队列在
Dart
中是很有必要的,因为有时候事件处理想要在稍后完成一些任务但又希望是在执行下一个事件消息之前。
Event队列包含Dart
和系统中其他位置的事件,MicroTask只包含Dart
的代码,那么Event Looper处理两个队列的顺序是如下图,当main
方法退出后,Event Looper就开始它的工作,首先会以FIFO的顺序执行MicroTask(先执行简短的异步任务),当所有的microtask执行完就会从Event队列去提取事件执行,这样反复,直到两个队列都是空。
这里要注意:虽然可以预测知道任务执行顺序,但是无法准确预测事件循环什么时候处理期望的任务。就好像创建了一个延时2s的任务,但是排在你之前的任务结束前事件处理是不会处理这个延时2s任务,也就是执行这个延时任务有可能大于2s。
1.4.通过链接方式指定任务顺序
new Future(() => futureTask) //异步任务的函数
.then((d) => "execute value:$d") //任务执行完后的子任务
.then((d) => d.length) //其中d为上个任务执行完后的返回的结果
.then((d) => printLength(d))
.whenComplete(() => whenTaskCompelete); //当所有任务完成后的回调函数
}
复制代码
可以看到,上述代码明确表示前后的依赖关系,可以使用then()()
来表明要使用变量就必须要等设置完这个变量。还可以使用whenComplete()
,异步完成时的回调。
1.5.Event队列
使用new Future
或者new Future.delayed()
来向Event队列添加事件,也就是说Future操作是通过Event队列来处理,如下面代码:
//向event队列中添加一个任务
new Future(() {
//具体任务
});
复制代码
想要在两秒后将任务添加到Event队列
// 两秒以后将任务添加至event队列
new Future.delayed(const Duration(seconds:2), () {
//任务具体代码
});
复制代码
因为上面说过,上面这个任务想要执行必须满足main
方法执行完,Misrotask队列是空的,这个任务之前的任务需要执行完,所以这个任务被执行有可能大于2秒。
1.6.MicroTask队列
scheduleMicrotask(() {
// 具体逻辑
});
复制代码
上面就是将一个任务加到MicroTask队列中去。
1.7.例子1
import 'dart:async';
main() {
print('main #1 of 2');
scheduleMicrotask(() => print('microtask #1 of 2'));
new Future.delayed(new Duration(seconds:1),
() => print('future #1 (delayed)'));
new Future(() => print('future #2 of 3'));
new Future(() => print('future #3 of 3'));
scheduleMicrotask(() => print('microtask #2 of 2'));
print('main #2 of 2');
}
复制代码
输出结果:
main #1 of 2
main #2 of 2
microtask #1 of 2
microtask #2 of 2
future #2 of 3
future #3 of 3
future #1 (delayed)
复制代码
上面执行顺序:main方法 ->Microtask队列->Event队列(先 new Future 后new Future.delay),下面直接拿官方的例子实践一下:
1.8.例子2
import 'dart:async';
main() {
print('main #1 of 2');
scheduleMicrotask(() => print('microtask #1 of 3'));
new Future.delayed(new Duration(seconds:1),
() => print('future #1 (delayed)'));
new Future(() => print('future #2 of 4'))
.then((_) => print('future #2a'))
.then((_) {
print('future #2b');
scheduleMicrotask(() => print('microtask #0 (from future #2b)'));
})
.then((_) => print('future #2c'));
scheduleMicrotask(() => print('microtask #2 of 3'));
new Future(() => print('future #3 of 4'))
.then((_) => new Future(
() => print('future #3a (a new future)')))
.then((_) => print('future #3b'));
new Future(() => print('future #4 of 4'));
scheduleMicrotask(() => print('microtask #3 of 3'));
print('main #2 of 2');
}
复制代码
输出结果:
main #1 of 2
main #2 of 2
microtask #1 of 3
microtask #2 of 3
microtask #3 of 3
future #2 of 4
future #2a
future #2b
future #2c
microtask #0 (from future #2b)
future #3 of 4
future #4 of 4
future #3a (a new future)
future #3b
future #1 (delayed)
复制代码
上面两个小例子会加深对事件消息的理解。
三、Dart中的异步支持
因为Dart
是单线程语言,当遇到延迟的运算(I/O操作),线程中顺序执行的运算就会阻塞,那就app上,用户操作就会感到卡顿,于是通常用异步处理来解决这个问题,当遇到需要延迟的运算时,就会放入延迟运算的队列中,先把不需要延迟的运算先执行,最后再来处理延迟运算。Dart
类库有非常多的返回Future
或者Stream
对象的函数,这些函数被称为异步函数;它们会在设置好一些需要消耗一定时间的操作之后返回,比如I/O操作,而不是等到这个操作完成。
1.Future
什么是Future
,顾名思义,表示一件将来会发生的事情(也就是不会立即执行),将来可以从Future
中取到一个值,当一个方法返回一个Future
的事情,发生两件事:
- 这个方法将某件事情排队,返回一个未完成的
Future
。 - 这个方法事情完毕后,
Future
的状态会变成已经完成,这个时候可以取到这件事情的返回值。
Future
表示一个异步操作的最终完成(或失败)及其结果值的表示,简单来说,它就是用来处理异步操作的,异步处理成功就执行成功的操作,异步处理失败就捕获错误或者停止后续操作,一个Future
只会对应一个结果,要么成功,要么失败。
1.1.Future.then
main() {
create();
}
//模执行延时任务
void create(){
//延迟三秒执行
Future.delayed(new Duration(seconds: 3),(){
return "This is data";
}).then((data){
print(data);
});
}
复制代码
输出结果如下:
This is data
复制代码
上面可以发现,使用Future.delayed
创建一个延时任务,当三秒后通过then
data接收了这个所返回的This is data
这个字符串的值。下面读取一个文件,先创建一个文件:
下面读取内容看看:
main() {
create();
}
void create(){
//延迟三秒执行
var file = File("/Users/luguian/Downloads/flutter第五天/flutter.rtf");
//定义了返回结果值为String类型
Future<String> data = file.readAsString();
//返回文件内容
data.then((text){
//打印文件内容
print(text);
});
print("I love Android");
}
复制代码
I love Android -->先打印I love Android
{\rtf1\ansi\ansicpg936\cocoartf1561\cocoasubrtf600
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset134 PingFangSC-Regular;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx566\tx1133\tx1700\tx2083\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
\f0\fs24 \cf0 I love flutter --->文字内容
\f1 ;}
复制代码
可以发现,先输出I love Android
,然后读取文件这种超时的操作会后执行,也就是读取文件内容是将来执行的,then
接收异步并打印出结果。注意:Future并不是并行执行的。
1.2.Future.catchError
当异步任务发生错误,可以在catchError
中捕获错误,例子如下:
Future.delayed(new Duration(seconds: 3),(){
throw AssertionError("This is a Error");
}).then((data){
//这是成功的逻辑
print("success");
}).catchError((e){
//失败会走到这里
print(e);
});
复制代码
输出结果如下:
Assertion failed
复制代码
可以发现,在异步任务中抛出了一个异常,then
的回调函数不会执行,反而catchError
函数被调用,当然并不是只有catchError
才能捕获错误,then
方法有一个可选的参数onError
,可以用它来捕获异常:
Future.delayed(new Duration(seconds: 3),(){
throw AssertionError("This is a Error");
}).then((data){
//这是成功的逻辑
print("success");
},onError:(e){
print(e);
});
复制代码
输出结果:
Assertion failed
复制代码
1.3.Future.whenComplete
有很多时候,当异步任务无论成功或者失败都需要做一些事的场景,如在网络请求前弹出加载进度框,在请求结束后关闭进度框,下面用whenComplete
进行回调,例子如下:
Future.delayed(new Duration(seconds: 3),(){
throw AssertionError("This is a Err