深入理解Flutter多线程

loadData();
}

loadData() async {
String dataURL = “https://jsonplaceholder.typicode.com/posts”;
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}

Future

Future就是延时操作的一个封装,可以将异步任务封装为Future对象。获取到Future对象后,最简单的方法就是用await修饰,并等待返回结果继续向下执行。正如上面async、await中讲到的,使用await修饰时需要配合async一起使用。

Dart中,和时间相关的操作基本都和Future有关,例如延时操作、异步操作等。下面是一个很简单的延时操作,通过Futuredelayed方法实现。

loadData() {
// DateTime.now(),获取当前时间
DateTime now = DateTime.now();
print(‘request begin $now’);
Future.delayed(Duration(seconds: 1), (){
now = DateTime.now();
print(‘request response $now’);
});
}

Dart还支持对Future的链式调用,通过追加一个或多个then方法来实现,这个特性非常实用。例如一个延时操作完成后,会调用then方法,并且可以传递一个参数给then。调用方式是链式调用,也就代表可以进行很多层的处理。这有点类似于iOS的RAC框架,链式调用进行信号处理。

Future.delayed(Duration(seconds: 1), (){
int age = 18;
return age;
}).then((onValue){
onValue++;
print(‘age $onValue’);
});

协程

如果想要了解asyncawait的原理,就要先了解协程的概念,asyncawait本质上就是协程的一种语法糖。协程,也叫作coroutine,是一种比线程更小的单元。如果从单元大小来说,基本可以理解为进程->线程->协程。

任务调度

在弄懂协程之前,首先要明白并发和并行的概念,并发指的是由系统来管理多个IO的切换,并交由CPU去处理。并行指的是多核CPU在同一时间里执行多个任务。

并发的实现由非阻塞操作+事件通知来完成,事件通知也叫做“中断”。操作过程分为两种,一种是CPU对IO进行操作,在操作完成后发起中断告诉IO操作完成。另一种是IO发起中断,告诉CPU可以进行操作。

线程本质上也是依赖于中断来进行调度的,线程还有一种叫做“阻塞式中断”,就是在执行IO操作时将线程阻塞,等待执行完成后再继续执行。但线程的消耗是很大的,并不适合大量并发操作的处理,而通过单线程并发可以进行大量并发操作。当多核CPU出现后,单个线程就无法很好的利用多核CPU的优势了,所以又引入了线程池的概念,通过线程池来管理大量线程。

协程

在程序执行过程中,离开当前的调用位置有两种方式,继续调用其他函数和return返回离开当前函数。但是执行return时,当前函数在调用栈中的局部变量、形参等状态则会被销毁。

协程分为无线协程和有线协程,无线协程在离开当前调用位置时,会将当前变量放在堆区,当再次回到当前位置时,还会继续从堆区中获取到变量。所以,一般在执行当前函数时就会将变量直接分配到堆区,而asyncawait就属于无线协程的一种。有线协程则会将变量继续保存在栈区,在回到指针指向的离开位置时,会继续从栈中取出调用。

async、await原理

asyncawait为例,协程在执行时,执行到async则表示进入一个协程,会同步执行async的代码块。async的代码块本质上也相当于一个函数,并且有自己的上下文环境。当执行到await时,则表示有任务需要等待,CPU则去调度执行其他IO,也就是后面的代码或其他协程代码。过一段时间CPU就会轮训一次,看某个协程是否任务已经处理完成,有返回结果可以被继续执行,如果可以被继续执行的话,则会沿着上次离开时指针指向的位置继续执行,也就是await标志的位置。

由于并没有开启新的线程,只是进行IO中断改变CPU调度,所以网络请求这样的异步操作可以使用asyncawait,但如果是执行大量耗时同步操作的话,应该使用isolate开辟新的线程去执行。

如果用协程和iOS的dispatch_async进行对比,可以发现二者是比较相似的。从结构定义来看,协程需要将当前await的代码块相关的变量进行存储,dispatch_async也可以通过block来实现临时变量的存储能力。

我之前还在想一个问题,苹果为什么不引入协程的特性呢?后来想了一下,awaitdispatch_async都可以简单理解为异步操作,OC的线程是基于Runloop实现的,Dart本质上也是有事件循环的,而且二者都有自己的事件队列,只是队列数量和分类不同。

我觉得当执行到await时,保存当前的上下文,并将当前位置标记为待处理任务,用一个指针指向当前位置,并将待处理任务放入当前isolate的队列中。在每个事件循环时都去询问这个任务,如果需要进行处理,就恢复上下文进行任务处理。

Promise

这里想提一下JS里的Promise语法,在iOS中会出现很多if判断或者其他的嵌套调用,而Promise可以把之前横向的嵌套调用,改成纵向链式调用。如果能把Promise引入到OC里,可以让代码看起来更简洁,直观。

isolate

isolateDart平台对线程的实现方案,但和普通Thread不同的是,isolate拥有独立的内存,isolate由线程和独立内存构成。正是由于isolate线程之间的内存不共享,所以isolate线程之间并不存在资源抢夺的问题,所以也不需要锁。

通过isolate可以很好的利用多核CPU,来进行大量耗时任务的处理。isolate线程之间的通信主要通过port来进行,这个port消息传递的过程是异步的。通过Dart源码也可以看出,实例化一个isolate的过程包括,实例化isolate结构体、在堆中分配线程内存、配置port等过程。

isolate看起来其实和进程比较相似,之前请教阿里架构师宗心问题时,宗心也说过“isolate的整体模型我自己的理解其实更像进程,而asyncawait更像是线程”。如果对比一下isolate和进程的定义,会发现确实isolate很像是进程。

代码示例

下面是一个isolate的例子,例子中新创建了一个isolate,并且绑定了一个方法进行网络请求和数据解析的处理,并通过port将处理好的数据返回给调用方。

loadData() async {
// 通过spawn新建一个isolate,并绑定静态方法
ReceivePort receivePort =ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);

// 获取新isolate的监听port
SendPort sendPort = await receivePort.first;
// 调用sendReceive自定义方法
List dataList = await sendReceive(sendPort, ‘https://jsonplaceholder.typicode.com/posts’);
print(‘dataList $dataList’);
}

// isolate的绑定方法
static dataLoader(SendPort sendPort) async{
// 创建监听port,并将sendPort传给外界用来调用
ReceivePort receivePort =ReceivePort();
sendPort.send(receivePort.sendPort);

// 监听外界调用
await for (var msg in receivePort) {
String requestURL =msg[0];
SendPort callbackPort =msg[1];

Client client = Client();
Response response = await client.get(requestURL);
List dataList = json.decode(response.body);
// 回调返回值给调用者
callbackPort.send(dataList);
}
}

// 创建自己的监听port,并且向新isolate发送消息
Future sendReceive(SendPort sendPort, String url) {
ReceivePort receivePort =ReceivePort();
sendPort.send([url, receivePort.sendPort]);
// 接收到返回值,返回给调用者
return receivePort.first;
}

isolate和iOS中的线程还不太一样,isolate的线程更偏底层。当生成一个isolate后,其内存是各自独立的,相互之间并不能进行访问。但isolate提供了基于port的消息机制,通过建立通信双方的sendPortreceiveport,进行相互的消息传递,在Dart中叫做消息传递。

从上面例子中可以看出,在进行isolate消息传递的过程中,本质上就是进行port的传递。将port传递给其他isolate,其他isolate通过port拿到sendPort,向调用方发送消息来进行相互的消息传递。

Embedder

正如其名,Embedder是一个嵌入层,将Flutter嵌入到各个平台上。Embedder负责范围包括原生平台插件、线程管理、事件循环等。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Embedder中存在四个Runner,四个Runner分别如下。其中每个Flutter Engine各自对应一个UI RunnerGPU RunnerIO Runner,但所有Engine共享一个Platform Runner

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Runnerisolate并不是一码事,彼此相互独立。以iOS平台为例,Runner的实现就是CFRunLoop,以一个事件循环的方式不断处理任务。并且Runner不只处理Engine的任务,还有Native Plugin带来的原生平台的任务。而isolate则由Dart VM进行管理,和原生平台线程并无关系。

Platform Runner

Platform Runner和iOS平台的Main Thread非常相似,在Flutter中除耗时操作外,所有任务都应该放在Platform中,Flutter中的很多API并不是线程安全的,放在其他线程中可能会导致一些bug。

但例如IO之类的耗时操作,应该放在其他线程中完成,否则会影响Platform的正常执行,甚至于被watchdog干掉。但需要注意的是,由于Embedder Runner的机制,Platform被阻塞后并不会导致页面卡顿。

不只是Flutter Engine的代码在Platform中执行,Native Plugin的任务也会派发到Platform中执行。实际上,在原生侧的代码运行在Platform Runner中,而Flutter侧的代码运行在Root Isolate中,如果在Platform中执行耗时代码,则会卡原生平台的主线程。

UI Runner

UI Runner负责为Flutter Engine执行Root Isolate的代码,除此之外,也处理来自Native Plugin的任务。Root Isolate为了处理自身事件,绑定了很多函数方法。程序启动时,Flutter Engine会为Root绑定UI Runner的处理函数,使Root Isolate具备提交渲染帧的能力。

Root IsolateEngine提交一次渲染帧时,Engine会等待下次vsync,当下次vsync到来时,由Root IsolateWidgets进行布局操作,并生成页面的显示信息的描述,并将信息交给Engine去处理。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节

还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一线互联网面试专题

379页的Android进阶知识大全

379页的Android进阶知识大全

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 16
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值