本节书摘来自华章出版社《深入理解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章来做更详尽的分析。