移动端的消息循环
一、Web
当打开浏览器输入网址打开网页经DNS解析获取到对应的ip地址,随后浏览器开始拉取资源,启动渲染进程,渲染进程启动主线程构建DOM tree、CSS tree,通过DOM tree和CSS tree构建render tree,后面还需要加载js代码,如果需要重载还需要重载;
因为渲染进程只有一个主线程,所以在里面跑的js只能是单线程;
主线程中会运行EventLoop专门作为消息循环来处理消息,结构为存放栈帧的栈队列(Stack),存放对象的堆内存(Heap),以及放置待处理的消息队列 (Message Queue);
每调用一次方法,该方法及参数将以 栈帧 的形式推到 栈内 ,相关引用对象变量则会放到 堆内存 中;
消息队列中会处理宏任务(macroTasks)和微任务(mircoTasks),任务会以 回调函数 Callback 按“一定的规则”放到消息队列中;
EventLoop 是一个自始至终运行的机制,它会等待消息队列是否还有任务加入,如果没有,会按“一定规则”运行消息队列中的回调函数,可以看作是一个一直在运行的死循环;
宏任务包括: script代码,setTimeout,setImmediate,I/O 等;
微任务包括:Promise,MutationObserver,process.nextTick(nodejs)等;
符合特定规则的 Web APIs 会专门放置到一个任务队列,依靠 EventLoop 机制,在主任务代码运行完后,不停轮询调用这些任务队列中的回调方法。这样我们的 js 代码不会是同步式,而是各种 Callback 回调风格的事件驱动式,所以js 是一种 非阻塞式I/O 的模型。
二、ReactNative
RN为JS Engine创建了一个独立的com.facebook.React.JavaScript异步后台线程,所有的js代码都运行在其中,所有的操作都不会阻塞UI线程;
js调用Native时会做线程检查,如果不在这个线程会抛出异常;
线程中有一个RunLoop处理消息,没有事情处理时会处于休眠状态,当有事件时会激活;
消息的处理类似于Web,会有一个MessageQueue缓存js->Native事件,完成Native->js后会以Callback驱动MessageQueue,缓存时间为MIN_TIME_BETWEEN_FLUSHES_MS = 5毫秒。
三、Flutter
Dart作为一个个事件在Isolate中运行,Isolate内部都有一个事件循环,Isolate之间不能直接互相访问,需要通过Port互相通讯,Isolate在一个线程池中的各个线程之间进行切换;
Isolate并不是一个死循环,只有一个处理消息的功能;
当有消息时,消息会首先插入消息队列MessageQueue,如果此时Isolate没有在运行,Dart VM会将消息处理器以任务的形式交给线程池,线程池会视情况为其分配一个线程,然后在分配的线程上执行消息处理器;
Isolate并不会长期占用一个线程,而是共用一个线程池,谁有任务谁就获得线程资源;
Isolate中存在两个消息队列,一个普通的消息队列,一个更高优先级的OOB队列,比如在当前的Isolate生成(spawn)一个新的Isolate,可以发送OOB消息去控制新的Isolate;
每个Isolate都有一个消息处理器,同时也会根据需要对外暴露多个Port,每个端口和消息是一一对应,消息根据Port投递到相应的Isolate消息处理器,若Port无效消息会被丢弃;
Dart VM中存在一个全局唯一的PortMap来统一管理各个端口的生命周期和消息的传递;
Dart消息分发是两个层面的,一个是Native层的消息处理器,其他线程或Isolate发过来的消息都在这里汇总,Native层的消息处理器在处理消息的时候再进入Dart层消息处理器做进一步分发;
Dart层中每个Isolate都有一个私有的_portMap,里面存储的ReceivePort和对应的handler,ReceivePort实现了Stream接口;
对应的handler在收到消息后会将消息数据写入ReceivePort,监听Stream的回调就能对消息数据做处理。
四、iOS
一个线程如果没有一个RunLoop来将线程保活那么就会导致执行完就结束,在App启动时主线程会在main函数中启用一个主的RunLoop;
RunLoop是一个调度任务和处理任务的事件循环;
RunLoop的目的是为了在有工作的时候让线程忙起来,而在没有工作的时候让线程进入休眠状态;
RunLoop实际上是一个对象,对象在循环中处理程序运行过程出现的各种事件(比如触摸事件,UI刷新事件,定时器事件,Selector事件)从而保持程序的持续运行并且让程序在没有事件处理的时候进入休眠状态,从而节省CPU资源达到提升程序性能的目的;
每个线程都只能有一个唯一的RunLoop与之对应,存储在一个全局的字典里面,key为线程,value为RunLoop,默认状态是不开启,在线程结束时销毁;
RunLoop有kCFRunLoopDefaultMode、UITrackingRunLoopMode、UIInitializationRunLoopMode、GSEventReceiveRunLoopMode、kCFRunLoopCommonModes等状态。
五、Android
ActivityThread 的 main() 函数中会调用 Looper 的 loop() 方法让 Looper 开始轮询消息;
loop() 方法中有一个死循环,死循环中会调用 MessageQueue 的 next() 方法获取下一条消息;
在 MessageQueue 的 next() 方法中,首先会调用 nativePollOnce() JNI 方法检查队列中是否有新的消息要处理,没有时线程就会被阻塞;
有的话就会尝试找出需要优先执行的异步线程,没有异步消息的话,就会判断消息是否到了要执行的时间,是的话就返回给 Looper 处理,否则重新计算消息的执行时间;
获取到消息后,loop() 方法就会调用 Message 的 target 的 dispatchMessage() 方法,target 其实就是发送 Message 的 Handler ;
最后就会调用 Message 的 recycleUnchecked() 方法回收处理完的消息。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!