热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

android异步编程,flutter异步编程事件循环、Isolate、Stream(流)

事件循环、Isolate开始前我们需要明白Dart是单线程的并且Flutter依赖于Dart如果你知道js中的eventloop将很好理解dart的整个异步过程先看一段代码impo

89896c95205c6cd9714888ce6cfae32f.png

事件循环、Isolate

开始前我们需要明白 Dart 是单线程的并且 Flutter 依赖于 Dart

如果你知道js 中的event loop 将很好理解dart的整个异步过程

先看一段代码

import 'dart:async';

Future eventLoop() async{

print('A');

Future((){

print('F');

scheduleMicrotask((){print('H');});

Future((){

print('M');

}).then((_){

print('N');

});

}).then((_){

print('G');

});

Future((){

print('I');

}).then((_){

print('J');

});

scheduleMicrotask(text1);

scheduleMicrotask((){print('D');});

print('B');

}

void text1() {

print('C');

scheduleMicrotask((){print('E');});

Future((){

print('K');

}).then((_){

print('L');

});

}

你只到输出结果吗

正确的输出顺序是: A B C D E F G H I J K L M N

eventLoop

1、MicroTask 队列

微任务队列,一般使用scheduleMicroTask方法向队列中添加

这是大多数时候你不必使用的东西。比如,在整个 Flutter 源代码中 scheduleMicroTask() 方法仅被引用了 7 次, 所以最好优先考虑使用 Event 队列

2、Event 队列

I/O、手势、绘图、计时器、流、futures等等异步操作都将进入event队列

尽量使用事件队列可以使微任务队列更短,降低事件队列卡死的可能性

代码执行顺序

首先我们知道dart是单线程的,所以dart的代码执行顺序是: 同步代码依次执行 碰到异步代码先进对应的队列中,然后继续执行下面的代码 当同步代码执行完毕,先去看MicroTask 队列中的任务,将MicroTask队列中的任务依次执行完毕 MicroTask中的任务执行完毕后,再去看Event 队列中的任务,event队列出一个任务 然后执行 , 然后回到第三步 循环 直到所有队列都清空

Isolate

Isolate 是 Dart 中的 线程, Flutter的代码都是默认跑在root isolate上的

「Isolate」在 Flutter 中并不共享内存。不同「Isolate」之间通过「消息」进行通信。

import 'dart:async';

import 'dart:io';

import 'dart:isolate';

import 'package:flutter/foundation.dart';

import 'package:flutter/material.dart';

//一个普普通通的Flutter应用的入口

//main函数这里有async关键字,是因为创建的isolate是异步的

void main() async{

runApp(MyApp());

//asyncFibonacci函数里会创建一个isolate,并返回运行结果

print(await asyncFibonacci(20));

}

//这里以计算斐波那契数列为例,返回的值是Future,因为是异步的

Future asyncFibonacci(int n) async{

//首先创建一个ReceivePort,为什么要创建这个?

//因为创建isolate所需的参数,必须要有SendPort,SendPort需要ReceivePort来创建

final response = new ReceivePort();

//开始创建isolate,Isolate.spawn函数是isolate.dart里的代码,_isolate是我们自己实现的函数

//_isolate是创建isolate必须要的参数。

await Isolate.spawn(_isolate,response.sendPort);

//获取sendPort来发送数据

final sendPort = await response.first as SendPort;

//接收消息的ReceivePort

final answer = new ReceivePort();

//发送数据

sendPort.send([n,answer.sendPort]);

//获得数据并返回

return answer.first;

}

//创建isolate必须要的参数

void _isolate(SendPort initialReplyTo){

final port = new ReceivePort();

//绑定

initialReplyTo.send(port.sendPort);

//监听

port.listen((message){

//获取数据并解析

final data = message[0] as int;

final send = message[1] as SendPort;

//返回结果

send.send(syncFibonacci(data));

});

}

int syncFibonacci(int n){

return n <2 ? n : syncFibonacci(n-2) &#43; syncFibonacci(n-1);

}

因为Root isolate会负责渲染&#xff0c;还有UI交互&#xff0c;如果我们有一个很耗时的操作呢&#xff1f;前面知道isolate里是一个event loop(事件循环)&#xff0c;如果一个很耗时的task一直在运行&#xff0c;那么后面的UI操作都被阻塞了&#xff0c;所以如果我们有耗时的操作&#xff0c;就应该放在isolate里&#xff01;

Stream(流)

什么是流&#xff1f;

1460000024501183 这个大机器就是StreamController&#xff0c;它是创建流的方式之一。 StreamController有一个入口&#xff0c;叫做sink sink可以使用add方法放东西进来&#xff0c;放进去以后就不再关心了。 当有东西从sink进来以后&#xff0c;我们的机器就开始工作 StreamController有一个出口&#xff0c;叫做stream 机器处理完毕后就会把产品从出口丢出来&#xff0c;但是我们并不知道什么时候会出来&#xff0c;所以我们需要使用listen方法一直监听这个出口 而且当多个物品被放进来了之后&#xff0c;它不会打乱顺序&#xff0c;而是先入先出

使用Stream

StreamController controller &#61; StreamController();

//监听这个流的出口&#xff0c;当有data流出时&#xff0c;打印这个data

StreamSubscription subscription &#61;

controller.stream.listen((data)&#61;>print("$data"));

controller.sink.add(123);

// 输出&#xff1a; 123

你需要将一个方法交给stream的listen函数&#xff0c;这个方法入参(data)是我们的StreamController处理完毕后产生的结果&#xff0c;我们监听出口&#xff0c;并获得了这个结果(data)。这里可以使用lambda表达式&#xff0c;也可以是其他任何函数

transform

如果你需要更多的控制转换&#xff0c;那么请使用transform()方法。他需要配合StreamTransformer进行使用。

StreamController controller &#61; StreamController();

final transformer &#61; StreamTransformer.fromHandlers(

handleData:(value, sink){

if(value&#61;&#61;100){

sink.add("你猜对了");

}

else{ sink.addError(&#39;还没猜中&#xff0c;再试一次吧&#39;);

}

});

controller.stream

.transform(transformer)

.listen(

(data) &#61;> print(data),

onError:(err) &#61;> print(err));

controller.sink.add(23);

//controller.sink.add(100);

// 输出&#xff1a; 还没猜中&#xff0c;再试一次吧

StreamTransformer是我们stream的检查员&#xff0c;他负责接收stream通过的信息&#xff0c;然后进行处理返回一条新的流。 S代表之前的流的输入类型&#xff0c;我们这里是输入一个数字&#xff0c;所以是int。 T代表转化后流的输入类型&#xff0c;我们这里add进去的是一串字符串&#xff0c;所以是String。 handleData接收一个value并创建一条新的流并暴露sink&#xff0c;我们可以在这里对流进行转化

Stream的种类 "Single-subscription" streams 单订阅流 "broadcast" streams 多订阅流

单订阅流

StreamController controller &#61; StreamController();

controller.stream.listen((data)&#61;> print(data));

controller.stream.listen((data)&#61;> print(data));

controller.sink.add(123);

// 输出: Bad state: Stream has already been listened to. 单订阅流不能有多个收听者。

单个订阅流在流的整个生命周期内仅允许有一个listener

多订阅流

StreamController controller &#61; StreamController();

//将单订阅流转化为广播流

Stream stream &#61; controller.stream.asBroadcastStream();

stream.listen((data)&#61;> print(data));

stream.listen((data)&#61;> print(data));

controller.sink.add(123);

// 输出&#xff1a; 123 123

广播流允许任意数量的收听者&#xff0c;且无论是否有收听者&#xff0c;他都能产生事件。所以中途进来的收听者将不会收到之前的消息。

参考 深入了解isolate



推荐阅读
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
author-avatar
不分手得恋爱假的_457
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有