前言
在前文《Flutter/Dart中的異步》里,我們知道了Flutter/Dart程序是事件驅(qū)動(dòng)的,Dart代碼都是以Isolate的形式存在。每個(gè)Isolate內(nèi)部都有一個(gè)事件循環(huán),
Dart代碼的運(yùn)行就是在不停的在處理一個(gè)又一個(gè)的事件。Isolate之間是不能直接互相訪問(wèn)的,它們之間需要通過(guò)來(lái)端口(Port)互相通訊。理解這個(gè)事件機(jī)制是理解Flutter/Dart運(yùn)行的基礎(chǔ)。這個(gè)事件機(jī)制就如同人體的神經(jīng)系統(tǒng)一樣,可以使程序的各個(gè)部分能協(xié)同運(yùn)轉(zhuǎn)。也能為我們回答以下這些問(wèn)題:
-
Isolate之間如何通過(guò)端口(Port)互相通訊? - 定時(shí)器
Timer以及微任務(wù)是如何工作的? - 程序的I/O是如何進(jìn)行的?
- 在
Isolate中做網(wǎng)絡(luò)請(qǐng)求為什么不會(huì)阻塞? - Flutter對(duì)Dart的事件機(jī)制做了哪些改造?
要得到以上問(wèn)題的答案就需要在了解Dart語(yǔ)言,F(xiàn)lutter框架的基礎(chǔ)上再深入學(xué)習(xí)Dart虛擬機(jī)以及Flutter引擎(Engine)部分的源碼。本文余下部分會(huì)在盡量不貼源碼的基礎(chǔ)上給大家介紹事件機(jī)制的底層實(shí)現(xiàn)。
事件機(jī)制
讀過(guò)Dart虛擬機(jī)相關(guān)文章的大家一定都看到過(guò)下面這張圖。
這是Google工程師Vyacheslav Egorov寫(xiě)的一篇介紹Dart虛擬機(jī)的博客《 Introduction to Dart VM》里的一張圖。從這張圖和博客里的說(shuō)明我們可以知道Dart代碼都是運(yùn)行的在
Isolate中的,從底層看執(zhí)行是在某一個(gè)Mutator Thread,也就是在某個(gè)具體線程中。但是Isolate與系統(tǒng)線程在整個(gè)程序生命周期內(nèi)并不是一一綁定的。一個(gè)Isolate現(xiàn)在運(yùn)行在線程池的某個(gè)線程中,過(guò)一會(huì)可能會(huì)運(yùn)行在線程池的另一個(gè)線程中。同樣的,對(duì)于一個(gè)線程池的線程來(lái)說(shuō),可能這會(huì)兒在運(yùn)行一個(gè)Isolate,過(guò)會(huì)兒會(huì)運(yùn)行另一個(gè)Isolate。但是有一點(diǎn)可以確定就是在某一時(shí)刻,一個(gè)Isolate只會(huì)運(yùn)行在一個(gè)系統(tǒng)線程中。從這種對(duì)應(yīng)關(guān)系可以看出,Isolate更像是運(yùn)行在線程池中的一個(gè)個(gè)任務(wù)。
Isolate的消息處理
那么Isolate又是如何在線程中運(yùn)行呢?從我們對(duì)于事件驅(qū)動(dòng)程序架構(gòu)的了解,就能預(yù)計(jì)這個(gè)線程中必然要運(yùn)行的是消息循環(huán)。有消息循環(huán)那就必然會(huì)有消息隊(duì)列,同樣的還要對(duì)外開(kāi)放接收消息的端口,這樣的話Isolate就可以用下圖來(lái)表示:

和一般情況不同的是,Isolate的消息循環(huán)并不是一個(gè)死循環(huán),而只有一個(gè)消息處理的功能。當(dāng)有外部消息到來(lái)的時(shí)候,消息首先會(huì)被插入消息隊(duì)列MessageQueue。如果此時(shí)Isolate并沒(méi)有在運(yùn)行的話,虛擬機(jī)會(huì)將消息處理器以任務(wù)的形式交給線程池,線程池會(huì)視情況為其分配一個(gè)線程,然后在分配的線程上開(kāi)始執(zhí)行任務(wù)處理器,也就是從隊(duì)列里取一個(gè)消息,處理一個(gè)消息,直到隊(duì)列為空。如果消息都處理完了,那么線程的任務(wù)也就執(zhí)行完了,這個(gè)線程也就空閑出來(lái)了,線程池有可能調(diào)度新的任務(wù)給它執(zhí)行,而這個(gè)新的任務(wù)有可能會(huì)是另一個(gè)Isolate的消息處理器。
可以看出Dart虛擬機(jī)在Isolate運(yùn)行這一塊的處理是相當(dāng)靈活的,Isolate并不會(huì)長(zhǎng)期占用一個(gè)線程,而是大家共用一個(gè)線程池,誰(shuí)有消息需要處理就獲得線程資源,沒(méi)有的話就不占用線程資源。
消息隊(duì)列
Isolate的消息處理器中存在著兩個(gè)消息隊(duì)列,一個(gè)隊(duì)列是普通消息隊(duì)列,另一個(gè)隊(duì)列叫OOB消息隊(duì)列,OOB是"out of band"縮寫(xiě),翻譯為帶外消息,OOB消息用來(lái)傳送一些控制類(lèi)消息,例如從當(dāng)前Isolate生成(spawn)一個(gè)新的Isolate。我們可以在當(dāng)前
Isolate發(fā)送OOB消息給新Isolate,從而控制新Isolate。比如,暫停(pause),恢復(fù)(resume),終止(kill)等。
OOB消息的優(yōu)先級(jí)是高于普通消息的,消息處理器在從消息隊(duì)列中獲取消息的時(shí)候會(huì)優(yōu)先從OOB消息隊(duì)列獲取消息,當(dāng)OOB消息隊(duì)列為空之后,才會(huì)從普通消息隊(duì)列中去獲取消息。
消息
對(duì)于需要被傳過(guò)來(lái)傳過(guò)去的消息來(lái)說(shuō),最重要的就是這個(gè)消息的目的地地址。在Dart中,這個(gè)地址就是Port。每個(gè)消息都會(huì)綁定一個(gè)目標(biāo)端口,只有這樣,這個(gè)消息才會(huì)被正確的投遞到相應(yīng)Isolate的消息處理器。如果這個(gè)端口是個(gè)無(wú)效端口的話,消息會(huì)被丟棄掉。
端口以及PortMap
從以上表述可知,Dart消息機(jī)制用來(lái)做尋址的就是端口Port。每個(gè)Isolate都會(huì)有一個(gè)消息處理器,同時(shí)Isolate也會(huì)根據(jù)需要對(duì)外暴露多個(gè)端口。每個(gè)端口都會(huì)和一個(gè)消息處理器綁定。
顯然在Dart虛擬機(jī)中會(huì)存在多種需要收發(fā)消息的情況,Isolate之間需要收發(fā)消息,Isolate需要接收I/O消息,以及定時(shí)器Timer消息等。這些消息往往要跨越不同的線程。Android采用的是"Looper-Handler"機(jī)制,而Dart虛擬機(jī)則采用一種更為直接的辦法。在虛擬機(jī)內(nèi)部存在著一個(gè)全局唯一的PortMap來(lái)同一管理各個(gè)端口的生命周期以及消息的傳遞。這樣的話每個(gè)線程都可以訪問(wèn)到PortMap,用來(lái)傳遞消息自然也不存在障礙了。
PortMap內(nèi)部維護(hù)者一個(gè)保存著所有端口信息的哈希表。這個(gè)哈希表的每個(gè)元素都有一個(gè)端口號(hào)和對(duì)應(yīng)的消息處理器。這樣的話只需要通過(guò)端口號(hào)來(lái)查這個(gè)表,就能得到消息處理器了,能找到消息處理器,自然也就可以將消息在消息處理器里入隊(duì)然后讓消息處理器去處理。

PortMap同時(shí)也管理者所有端口的生命周期,每個(gè)端口的創(chuàng)建和關(guān)閉都需要通過(guò)PortMap來(lái)操作。以Isolate為例,當(dāng)我們?cè)?code>Isolate中新建一個(gè)ReceivePort的時(shí)候,這個(gè)調(diào)用最后會(huì)來(lái)到PortMap這里。PortMap會(huì)生成一個(gè)端口號(hào),把這個(gè)端口號(hào)與當(dāng)前Isolate的消息處理器(Message Handler)綁定然后保存在PortMap的哈希表里面。
從Dart虛擬機(jī)的實(shí)現(xiàn)來(lái)看,PortMap在虛擬機(jī)初始化的時(shí)候就會(huì)初始化。其內(nèi)部會(huì)有一個(gè)隨機(jī)數(shù)生成器,每當(dāng)要?jiǎng)?chuàng)建新端口的時(shí)候就會(huì)隨機(jī)生成一個(gè)端口號(hào)。
關(guān)閉端口的時(shí)候會(huì)將端口號(hào)對(duì)應(yīng)的元素從哈希表中刪除。
當(dāng)線程需要向外發(fā)送消息的時(shí)候,會(huì)調(diào)用PortMap::PostMessage()根據(jù)端口號(hào)來(lái)查詢(xún)哈希表,找到端口對(duì)應(yīng)的消息處理器之后就可以將消息入隊(duì)進(jìn)行處理了。消息傳遞過(guò)程如下圖所示

從
PortMap使用全局哈希表來(lái)存儲(chǔ)端口信息我們可以想到,端口在不再需要的時(shí)候要關(guān)閉,進(jìn)一步的,Isolate在不再需要的時(shí)候也要及時(shí)殺掉,否則可能會(huì)使資源不能及時(shí)釋放,從而造成“泄漏”。
消息分發(fā)
Dart的消息分發(fā)是分為兩個(gè)層面的,一個(gè)是在Native層的消息處理器,其他線程或者Isolate發(fā)過(guò)來(lái)的消息都會(huì)首先匯聚到這里,Native層的消息處理器在處理消息的時(shí)候再進(jìn)入Dart層消息處理器做進(jìn)一步的分發(fā)。如果把整個(gè)Isolate所在的線程比作一個(gè)小區(qū),Dart層每個(gè)監(jiān)聽(tīng)的每個(gè)ReceivePort比作小區(qū)住戶(hù)的話,那么Native層的消息處理器就是小區(qū)大門(mén),消息要入戶(hù)還需要在Dart層做進(jìn)一步分發(fā)。

在Dart層,每個(gè)Isolate會(huì)有一個(gè)自己私有的_portMap,里面存儲(chǔ)的也是ReceivePort端口號(hào)和對(duì)應(yīng)的handler。我們知道ReceivePort實(shí)現(xiàn)了Stream接口。對(duì)應(yīng)的handler在收到消息以后會(huì)將消息數(shù)據(jù)寫(xiě)入ReceivePort,這樣監(jiān)聽(tīng)這個(gè)Stream的回調(diào)就能對(duì)消息數(shù)據(jù)做處理了,Dart層消息處理代碼如下:
@pragma("vm:entry-point", "call")
static void _handleMessage(Function handler, var message) {
handler(message);
_runPendingImmediateCallback();
}
消息處理做了兩件事,首先是處理從端口過(guò)來(lái)的消息handler(message)。然后還有一個(gè)
_runPendingImmediateCallback();。這個(gè)函數(shù)調(diào)用會(huì)處理所有的微任務(wù)。這也就是前言里面那張事件循環(huán)圖的由來(lái)。
從以上描述我們也可以看出,Port消息機(jī)制是單向的,這也是為什么我們通常在spawn一個(gè)新的Isolate的時(shí)候會(huì)創(chuàng)建一個(gè)新的ReceivePort,然后把相應(yīng)的SendPort交給子Isolate。這樣就建立起了子Isolate到父Isolate的通道,如果需要雙向通信的話,子Isolate也要?jiǎng)?chuàng)建自己的ReceivePort,把對(duì)應(yīng)的SendPort通過(guò)上一個(gè)通道傳給父Isolate。聽(tīng)起來(lái)比較繞,但是明白了消息機(jī)制以后理解起來(lái)就容易多了。
Timer機(jī)制
定時(shí)器Timer是另一個(gè)重要的事件來(lái)源。Dart虛擬使用EventHandler來(lái)管理定時(shí)器資源。要使用定時(shí)功能,就必須要調(diào)用系統(tǒng)底層資源,為此,Dart虛擬機(jī)在初始化的時(shí)候會(huì)初始化EventHandler,EventHandler會(huì)專(zhuān)門(mén)開(kāi)一個(gè)線程來(lái)提供定時(shí)器功能。這個(gè)線程被命名為"dart:io EventHandler"。由于對(duì)底層系統(tǒng)的依賴(lài),不同系統(tǒng)的實(shí)現(xiàn)也有所不同,以Android為例,定時(shí)器功能在底層依賴(lài)的是epoll機(jī)制。
顯然Isolate要使用定時(shí)器功能,就需要和EventHandler相互通訊。Isolate需要通知EventHandler來(lái)設(shè)置/取消定時(shí)器,而當(dāng)定時(shí)器到點(diǎn)的時(shí)候,EventHandler要將這一消息發(fā)送給Isolate。
先來(lái)說(shuō)從Isolate到EventHandler,根據(jù)前述的消息機(jī)制,似乎EventHandler需要先在PortMap里開(kāi)一個(gè)端口,然后Isolate通過(guò)這個(gè)端口給EventHandler發(fā)消息。然而這是不必要的,因?yàn)?code>EventHandler是全局唯一的。要給EventHandler發(fā)消息是不需要經(jīng)過(guò)PortMap的。直接調(diào)用EventHandler提供的EventHandler_SendData方法就可以了。
但是反過(guò)來(lái)從EventHandler到Isolate就需要端口了,否則EventHandler不知道要給誰(shuí)發(fā)消息,這個(gè)端口號(hào)需要在上一步Isolate到EventHandler消息里以參數(shù)的形式送過(guò)來(lái)。

在Dart層,Isolate的所有定時(shí)器都由_Timer管理。在存在有效定時(shí)器的時(shí)候,_Timer會(huì)開(kāi)放一個(gè)ReceivePort以便接收定時(shí)器到時(shí)消息。
我們知道定時(shí)器的使用分為兩類(lèi),一類(lèi)是帶延時(shí)的,另一類(lèi)是不帶延時(shí),或者說(shuō)延時(shí)為0的定時(shí)器。對(duì)這兩類(lèi)定時(shí)器_Timer也采用了不同的管理策略。
如果新建的定時(shí)器是無(wú)延時(shí)的,
_Timer會(huì)將其插入一個(gè)叫ZeroTimer的鏈表。如果新建的定時(shí)器是有延時(shí)的,
_Timer會(huì)將其插入一個(gè)叫_TimerHeap的二叉堆。堆頂就是最近到點(diǎn)的計(jì)時(shí)器。
_Timer對(duì)這兩類(lèi)計(jì)時(shí)器的處理也是不同的:
無(wú)延時(shí)定時(shí)器在被插入
ZeroTimer鏈表后會(huì)通過(guò)_sendPort給自己發(fā)一個(gè)_ZERO_EVENT消息。有延時(shí)的定時(shí)器在被插入
_TimerHeap二叉堆后,會(huì)檢查當(dāng)前定時(shí)器是不是最近要到點(diǎn)的,如果是的話,就會(huì)給EventHandler發(fā)送消息,消息里會(huì)帶上sendPort和最近要喚醒的時(shí)間。到點(diǎn)后EventHandler會(huì)發(fā)回來(lái)_TIMEOUT_EVENT消息。
_Timer自帶消息處理器,而不使用前述的通用消息處理器,在消息到來(lái)之后,消息處理器首先要找出當(dāng)前需要處理的定時(shí)器列表pendingTimers:
收到
_ZERO_EVENT,先取二叉堆中所有比當(dāng)前無(wú)延時(shí)定時(shí)器還早超時(shí)的定時(shí)器加入列表,最后將當(dāng)前無(wú)延時(shí)定時(shí)器也加入列表。收到
_TIMEOUT_EVENT,如果存在無(wú)延時(shí)定時(shí)器,則會(huì)將二叉堆中所有比當(dāng)前無(wú)延時(shí)定時(shí)器還早超時(shí)的定時(shí)器加入列表;如果不存在無(wú)延時(shí)定時(shí)器,則會(huì)將二叉堆中所有比當(dāng)前系統(tǒng)時(shí)間還早超時(shí)的定時(shí)器加入列表。
拿到需要處理的定時(shí)器列表pendingTimers后,消息處理器會(huì)挨個(gè)調(diào)用每個(gè)計(jì)時(shí)器的回調(diào)函數(shù)并更新其狀態(tài),如果有周期定時(shí)器還要再重新入堆。
最后,為了滿(mǎn)足Dart事件循環(huán)的設(shè)計(jì)要求,每完成一個(gè)定時(shí)器的回調(diào)之后都要調(diào)用_runPendingImmediateCallback()來(lái)清空微任務(wù)隊(duì)列。
從上述定時(shí)器工作過(guò)程我們也能看到,只有有延時(shí)的定時(shí)器才會(huì)通過(guò)EventHandler去在底層做設(shè)置,無(wú)延時(shí)的定時(shí)器完全是在Dart層由_Timer自行處理。而有延時(shí)的定時(shí)器也只會(huì)將最近將要超時(shí)的一個(gè)定時(shí)器發(fā)送給EventHandler管理,其余的有更長(zhǎng)延時(shí)的定時(shí)器由_Timer自行在二叉堆中管理,這樣設(shè)計(jì)也是為了節(jié)省系統(tǒng)資源。
I/O機(jī)制
系統(tǒng)I/O同樣也是重要的事件來(lái)源,Dart的I/O機(jī)制自身細(xì)節(jié)是比較復(fù)雜的,本小結(jié)只會(huì)從消息傳遞的角度對(duì)I/O機(jī)制做一些闡述,具體的文件,目錄,http,socket等I/O方式實(shí)現(xiàn)細(xì)節(jié)還需要仔細(xì)學(xué)習(xí)源碼。
虛擬機(jī)在Dart層提供了_IOService來(lái)統(tǒng)一處理所有I/O請(qǐng)求。Dart層所有I/O操作,如文件的讀寫(xiě),網(wǎng)絡(luò)請(qǐng)求等都會(huì)歸集到_IOService從而轉(zhuǎn)至Native層進(jìn)行處理。_IOService給每種I/O操作都定義了一個(gè)編號(hào),例如打開(kāi)文件操作被定義為static const int fileOpen = 5,此類(lèi)中一共定義了43種I/O操作,具體可查看_IOService源碼。
所有I/O操作都是異步返回的,也就是說(shuō)發(fā)起I/O操作的Isolate和底層具體執(zhí)行的Native代碼之間是通過(guò)消息系統(tǒng)來(lái)互相溝通的。下面我們就來(lái)說(shuō)說(shuō)他們之間的通信通道是怎么建立起來(lái)的。
在接收到上層來(lái)的I/O調(diào)用請(qǐng)求時(shí),_IOService首先確保自己先完成初始化。這個(gè)初始化的主要是確保自己有一個(gè)ReceivePort,沒(méi)有就創(chuàng)建一個(gè)。這個(gè)ReceivePort就用來(lái)接收所有的I/O消息。
好了,現(xiàn)在有了本端的接收端口,那接下來(lái)就是對(duì)方的Native端接收端口了。這個(gè)Native端接收端口是由_IOService通過(guò)調(diào)用IOService_NewServicePort在Native層去創(chuàng)建,同樣的最終也要由PortMap做創(chuàng)建的工作。而我們知道在PortMap內(nèi)部一個(gè)端口必須要綁定一個(gè)MessageHandler。這個(gè)底層端口綁定的并不是Isolate的消息處理器,而是專(zhuān)為處理Native消息服務(wù)的NativeMessageHandler。NativeMessageHandler和IsolateMessageHandler都是繼承自MessageHandler。所以在Native層面其消息處理也是在線程池中進(jìn)行的。也就是說(shuō)上述那些具體的I/O操作,例如打開(kāi)文件,是在線程池里完成的。
Native端的ReceivePort,或者我們可以稱(chēng)之為ServicePort創(chuàng)建完成之后,其對(duì)應(yīng)的SendPort會(huì)被返回給Dart層。
所以現(xiàn)在的狀態(tài)是Dart層和Native層都有了ReceivePort,并且Dart層也拿到了Native層的SendPort。接下來(lái)要發(fā)起I/O請(qǐng)求就比較簡(jiǎn)單了,只需要調(diào)用SendPort.send將請(qǐng)求I/O操作的參數(shù)(比如上面說(shuō)的fileOpen = 5),相應(yīng)的參數(shù)(比如文件路徑)和Dart層sendport(I/O操作完成后Native需要這個(gè)sendport來(lái)通知Dart層操作的結(jié)果。)組合成消息送給NativeMessageHandler。NativeMessageHandler接收到消息以后會(huì)在線程池中處理這個(gè)消息,處理的時(shí)候會(huì)檢索請(qǐng)求I/O操作的參數(shù),然后找到對(duì)應(yīng)的底層I/O完成操作。完成之后再通過(guò)sendport給Dart層發(fā)消息告知操作結(jié)果。這樣一整個(gè)I/O操作流程就完整了。如圖所示:

因?yàn)镈art層接收消息走的還是原來(lái)的路徑,所以I/O操作也是滿(mǎn)足Dart事件循環(huán)的準(zhǔn)則的。
小結(jié)
至此,對(duì)Dart虛擬機(jī)的事件機(jī)制就介紹的差不多了。了解了整個(gè)消息系統(tǒng)的運(yùn)行機(jī)理,相信大家對(duì)Dart虛擬機(jī)的結(jié)構(gòu)不會(huì)再感到陌生了。對(duì)于前言里的那張事件驅(qū)動(dòng)示意圖會(huì)有更加深刻的了解。
Flutter是基于Dart虛擬機(jī),但上述消息機(jī)制并不能滿(mǎn)足Flutter的需求,所以Flutter對(duì)
Dart虛擬機(jī)的消息機(jī)制做了一些改造。下面的章節(jié)就簡(jiǎn)單介紹一下Flutter對(duì)Dart虛擬機(jī)的定制。
Flutter的定制
我們都知道Flutter在啟動(dòng)的時(shí)候會(huì)創(chuàng)建三個(gè)線程,分別是UI,GPU和IO,再加上原生的Platform線程,這四個(gè)線程互相協(xié)調(diào),共同撐起了Flutter運(yùn)行的基礎(chǔ)。其中UI線程會(huì)運(yùn)行RootIsolate。在RootIsolate中會(huì)運(yùn)行Flutter框架,也就是我之前的Flutter框架分析系列文章里所說(shuō)的渲染流水線。RootIsolate如此重要,顯然不能像普通的Isolate那樣把它的消息處理器扔給線程池去跑。而是指定在UI線程中運(yùn)行RootIsolate的MessageHandler。
消息處理定制
而這種指定是如何做到的呢?那就是RootIsolate在啟動(dòng)的時(shí)候有兩個(gè)地方和普通的Isolate不一樣之處。
一個(gè)是在RootIsolate初始化的時(shí)候,會(huì)把UITaskRunner設(shè)置給RootIsolate,最終會(huì)給RootIsolate的MessageHandler設(shè)置message_notify_callback_。
如此設(shè)置之后就會(huì)將RootIsolate的MessageHandler引導(dǎo)在UI線程運(yùn)行。
另一個(gè)就是要禁止RootIsolate的MessageHandler在線程池上運(yùn)行。這又是如何做到的呢?普通Isolate在運(yùn)行Dart代碼之前需要調(diào)用MessageHandler.run(),這個(gè)函數(shù)調(diào)用會(huì)給MessageHandler設(shè)置線程池。但RootIsolate是沒(méi)有調(diào)用這個(gè)函數(shù)的,而是跳過(guò)這一步直接開(kāi)始運(yùn)行Dart代碼。所以RootIsolate的MessageHandler是沒(méi)有線程池的,它的消息處理器只能運(yùn)行在UI線程上。
// 這個(gè)函數(shù)調(diào)用會(huì)設(shè)置線程池
void MessageHandler::Run(ThreadPool* pool, ...) {
...
pool_ = pool;
...
const bool launched_successfully = pool_->Run<MessageHandlerTask>(this);
}
決定消息處理器運(yùn)行在哪個(gè)線程的奧秘就在于函數(shù)MessageHandler.PostMessage():
void MessageHandler::PostMessage(std::unique_ptr<Message> message,bool before_events) {
// 正常消息入隊(duì)
if (message->IsOOB()) {
oob_queue_->Enqueue(std::move(message), before_events);
} else {
queue_->Enqueue(std::move(message), before_events);
}
...
// 普通ioslate由于線程池不為空,會(huì)進(jìn)入下方語(yǔ)句塊,在線程池上運(yùn)行消息處理器。
// `RootIsolate`由于沒(méi)有線程池,會(huì)跳過(guò)。
if (pool_ != nullptr && !task_running_) {
const bool launched_successfully = pool_->Run<MessageHandlerTask>(this);
}
//對(duì)于`RootIsolate`通過(guò)。調(diào)用以下函數(shù),最終會(huì)是其消息處理器運(yùn)行在UI線程。
MessageNotify(saved_priority);
}
Flutter通過(guò)上述兩處定制,就使RootIsolate的MessageHandler運(yùn)行在UI線程上了。這里我們需要明確一點(diǎn),整個(gè)消息處理的邏輯還是沒(méi)有變的,變化的只是運(yùn)行的線程。
微任務(wù)定制
Flutter對(duì)另外一處消息機(jī)制的定制是對(duì)微任務(wù)的處理。原生Isolate的微任務(wù)調(diào)度以及執(zhí)行都是在Dart層。切入點(diǎn)是在Dart層消息處理函數(shù)處理完一個(gè)消息之后執(zhí)行_runPendingImmediateCallback()。
Flutter在初始化RootIsolate會(huì)把Dart層調(diào)度微任務(wù)的函數(shù)設(shè)置成Native層的ScheduleMicrotask。如此一來(lái),微任務(wù)執(zhí)行的觸發(fā)也被挪到了Native層。當(dāng)UIDartState::FlushMicrotasksNow被調(diào)用以后就會(huì)開(kāi)始微任務(wù)執(zhí)行。
在Flutter中觸發(fā)微任務(wù)執(zhí)行的時(shí)機(jī)有兩處。一處是每當(dāng)UITaskRunner執(zhí)行完一個(gè)任務(wù)以后會(huì)觸發(fā)微任務(wù)。從上面對(duì)Flutter消息機(jī)制的分析我們了解到RootIsolate的消息處理器變成了由UITaskRunner運(yùn)行的一個(gè)任務(wù)。而且消息處理器每次只會(huì)處理一個(gè)正常消息,這樣的話依然滿(mǎn)足Dart事件循環(huán)的標(biāo)準(zhǔn)。
另一處是在engine回調(diào)_beginFrame之后和回調(diào)_drawFrame之前。在這兩個(gè)回調(diào)之間會(huì)觸發(fā)微任務(wù)執(zhí)行。關(guān)于這兩個(gè)回調(diào)請(qǐng)參考《# Flutter框架分析(一)-- 總覽和Window》。
總結(jié)
本文從虛擬機(jī)底層角度介紹了Dart事件機(jī)制的運(yùn)行原理和定時(shí)器事件,I/O事件的實(shí)現(xiàn)以及Flutter對(duì)原生Dart事件機(jī)制的定制。事件機(jī)制就如同循環(huán)系統(tǒng)之于動(dòng)物,道路系統(tǒng)之于城市。了解了事件機(jī)制之后,再去看Dart/Flutter內(nèi)部的各個(gè)功能模塊就會(huì)如庖丁解牛一樣輕松愉快。
(全文完)