本節書摘來自華章出版社《深入了解android》一書中的第3章,第3.6節,作者孟德國 王耀龍 周金利 黎歡,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視
android平台上的wtf庫提供了線程結構的c++封裝,本節要分析webkit的運作時線程結構、單個線程的實作結構,以及webkit運作時多個線程的同步及互動。
webkit線程的經典實作結構為:線程入口函數包含一個循環loop,loop 内部包含一個messagequeue.waitformessage();操作,這樣在沒有等待處理的消息時,将線程挂起在messagequeue内部封裝的一個threadcondition上,而在有消息時循環處理消息。messagequeue運作原理如圖3-3所示。
下述代碼是messagequeue的主要部分。
【→messagequeue.h】

浏覽messagequeue資料成員發現,内部包含一個deque作為主要的存儲結構。mutex類型m_mutex成員為多線程通路deque的互斥鎖,這裡mutex是對phread_mutex_t類型的c++封裝,配合muexlocker類型的局部變量在局部作用域内的構造和自動析構實作自動加鎖與解鎖。threadcondition是對pthread_cond_t類型的c++封裝,threadcondition類型的m_condition作為事件通知信号,同時提供了挂起線程的場所。messagequeue可以看作是線程安全deque的經典實作。
messagequeue的核心函數是append與waitformessagefilteredwithtimeout,這兩個函數都比較簡單,讀者可自行分析。
在webkit運作過程中,有衆多的操作必須交給主線程來做,比如回調javascript的接口。webkit運作時,大量的到主線程的異步回調,向主線程傳遞task 及參數時,必定直接或者間接調用函數callonmainthread。當然主線程交給其他線程的任務函數及參數的傳遞,也是采用非常類似的實作,本節主要分析callonmainthread及相關封裝函數的實作。
首先看一下callonmainthread的定義:
【→mainthread.cpp】
從上面的實作可以看出,callonmainthread的實作較簡單,其關聯的主要内容在sched-uledispatchfunctionsonmainthread,該函數及其觸發的後續内容會獨立分析,此處我們隻要知道callonmainthread将一個mainthreadfunction的指針放入mainthread的排程隊列中。
callonmainthread函數參數決定了能夠放到mainthread上執行的函數必須是單參數且無傳回值的普通函數或類内部的static方法。但是,如何将類内部的普通成員方法投放到mainthread的執行隊列呢?這種場景的實作方法有很多,本節主要分析chromium-base中使用的實作方式:
[→示例代碼]
在上面的代碼中,runtask作為普通c函數,被投放到mainthead中執行,在其内部間接調用被task封裝了的類普通成員函數class::function。下面分析newrunablemethod的使用與實作。
newrunablemethod函數根據參數類型有很多的重載版本,但總體上可以分為兩大類:第一類的第一個參數是函數指針,其餘參數為該函數指針所指函數的參數;第二類的第一個參數是對象指針,第二個參數是函數指針,其餘參數為函數指針所指函數的參數。上面示例代碼中使用的是第二類并且函數指針所指函數(該場景中就是class::function)含有兩個參數的情況。newrunablemethod函數将這些資訊封裝到類runablemethod中,并将生成的runablemethod對象傳回。細心的讀者透過runtask函數的定義一定猜到了runablemethod直接或者間接繼承自task,并且提供了virtual的run函數的實作。
我們看一下 runablemethod::run的實作。
【→external/chromium/base/task.h】
下面是dispatchtomethod的定義:
[→external/chromium/base/tuple.h]
這樣經過層層封裝就實作了将類的成員函數放到mainthread執行。
在webkit中還有一類常用的傳遞異步回調函數的方法,其核心是createcallbacktask函數,該函數也是将那些要被跨線程回調的函數封裝成crossthreadtaskx,這裡的x對應于參數的個數。這一系列crossthreadtaskx類實作了繼承自scriptexecutioncontext::task類的performtask方法,performtask内的動作也是調用被封裝的回調函數。這樣,在createcallbacktask産生的crossthreadtaskx被放到某個線程的任務隊列後,回調函數就會在恰當的時機被回調。
webkit運作時包含衆多的線程,比如負責網絡資源加載的線程、負責解析及頁面布局的線程、負責繪制的線程、負責檔案讀寫的線程、負責媒體資源編解碼的線程、worker線程等。其中,最重要的線程就是負責解析及頁面布局的線程,它生成并觸發其他線程的動作,作為webkit運作的中樞驅動了webkit的大多數動作。這個線程還有另外兩個響亮的名字:webcorethread和mainthread。
android平台的webkit的framework部分提供了webview接口,webviewcore随着webview的建立而建立,webviewcore在ui線程中構造時建立了一個java層線程,名字叫作“webcorethread”,随後在該線程初始化webviewcore,建立browserframe,而browserframe調用函數nativecreateframe,正式進入webkit的c++世界,并且初始化c++部分webkit的運作環境。
webcoreframebridge.cpp中的createframe也就是java層nativecreateframe對應的c++層函數,該函數一開始調用scriptcontroller::initializethreading(),在其内部通過wtf::initializemainthread,将目前線程的identifier賦給了mainthreadidentifier,也就是說目前線程就是mainthread。mainthreadidentifier的主要作用是通過與線程的标示符比較來确定被比較線程是否為主線程。據此可知,webcorethread也就是mainthread既有java層的部分,也有c++部分。
c++層的webkit擁有自己的循環内的messagequeue排程隊列,這個可以從###3.6.2節對callonmainthread的分析中得知,由于mainthread建立自java層,最終驅動該線程運作的消息循環也是在java層。在3.6.2節分析callonmainthread函數時,留下其内部調用的排程函數scheduledispatchfunctionsonmainthread()沒有分析。跟蹤這個函數的調用流程可以發現,最終被調用的java層代碼在jwebcorejavabridge.java中,該類處理了callonmainthread觸發的函數排程事件,以及timer事件等,也就是主線程native層的函數排程及timer事件都從該類觸發,并在恰當的時機回調native層要處理的函數。
關于android webkit的framework部分,将會在第8章來做更詳盡的分析。