《深入理解Android》一3.6 WebKit运行时线程结构

简介:

本节书摘来自华章出版社《深入理解Android》一书中的第3章,第3.6节,作者孟德国 王耀龙 周金利 黎欢,更多章节内容可以访问云栖社区“华章计算机”公众号查看

3.6 WebKit运行时线程结构

Android平台上的WTF库提供了线程结构的C++封装,本节要分析WebKit的运行时线程结构、单个线程的实现结构,以及WebKit运行时多个线程的同步及交互。

3.6.1 MessageQueue实现分析

WebKit线程的经典实现结构为:线程入口函数包含一个循环loop,loop 内部包含一个messageQueue.waitForMessage();操作,这样在没有等待处理的消息时,将线程挂起在messageQueue内部封装的一个ThreadCondition上,而在有消息时循环处理消息。MessageQueue运行原理如图3-3所示。
下述代码是MessageQueue的主要部分。
【→MessageQueue.h】

template<typename DataType>
class MessageQueue {
public:
     MessageQueue() : m_killed(false) { }
    ~MessageQueue();

    void append(PassOwnPtr<DataType>);

    PassOwnPtr<DataType> waitForMessage();
    template<typename Predicate>
    PassOwnPtr<DataType>
    waitForMessageFilteredWithTimeout(MessageQueueWaitResult&, Predicate&, double absoluteTime);

    // isEmpty()函数的返回值,只有在其它线程不访问该MessageQueue对象时才有意义
    bool isEmpty();

    static double infiniteTime() 
    { 
        return std::numeric_limits<double>::max(); 
    }

private:
    mutable Mutex m_mutex;
    ThreadCondition m_condition;
    Deque<DataType*> m_queue;
    bool m_killed;
};

image

浏览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,这两个函数都比较简单,读者可自行分析。

3.6.2 Task传递

在WebKit运行过程中,有众多的操作必须交给主线程来做,比如回调JavaScript的接口。WebKit运行时,大量的到主线程的异步回调,向主线程传递Task 及参数时,必定直接或者间接调用函数callOnMainThread。当然主线程交给其他线程的任务函数及参数的传递,也是采用非常类似的实现,本节主要分析callOnMainThread及相关封装函数的实现。
首先看一下callOnMainThread的定义:
【→MainThread.cpp】

typedef void MainThreadFunction(void*);
void callOnMainThread(MainThreadFunction* function, void* context)
{
    ASSERT(function);
    bool needToSchedule = false;
    {
        MutexLocker locker(mainThreadFunctionQueueMutex());
        needToSchedule = functionQueue().size() == 0;
        functionQueue().append(FunctionWithContext(function, context));
    }
    if (needToSchedule)
        scheduleDispatchFunctionsOnMainThread();
}

从上面的实现可以看出,callOnMainThread的实现较简单,其关联的主要内容在sched-uleDispatchFunctionsOnMainThread,该函数及其触发的后续内容会独立分析,此处我们只要知道callOnMainThread将一个MainThreadFunction的指针放入MainThread的调度队列中。
callOnMainThread函数参数决定了能够放到MainThread上执行的函数必须是单参数且无返回值的普通函数或类内部的static方法。但是,如何将类内部的普通成员方法投放到MainThread的执行队列呢?这种场景的实现方法有很多,本节主要分析chromium-base中使用的实现方式:
[→示例代码]

Static void RunTask(void * v){
OwnPtr<Task>  task(static_cast<Task*> (v));
        task->Run();
}
callOnMainThread(RunTask,NewRunableMethod(this,&CLASS::FUNCTION,para1,para2));

在上面的代码中,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】

template <class Method, class Params>
class RunableMethod: public CancelableTask{
……
virtual void Run() {
    if(obj_)  {//承载了NewRunableMethod第一个对象参数
        DispatchToMethod(obj_.get(), meth_, params_);
}
}
……
};

下面是DispatchToMethod的定义:
[→external/chromium/base/tuple.h]

template <class ObjT, calss Method, class A, class B>
inline void DispatchToMethod(ObjT * obj, Method method, const Tuple2<A,B>& arg){
    (obj->*method)(arg.a,arg,b);
}

这样经过层层封装就实现了将类的成员函数放到MainThread执行。
在WebKit中还有一类常用的传递异步回调函数的方法,其核心是createCallbackTask函数,该函数也是将那些要被跨线程回调的函数封装成CrossThreadTaskX,这里的X对应于参数的个数。这一系列CrossThreadTaskX类实现了继承自ScriptExecutionContext::Task类的performTask方法,performTask内的动作也是调用被封装的回调函数。这样,在createCallbackTask产生的CrossThreadTaskX被放到某个线程的任务队列后,回调函数就会在恰当的时机被回调。

3.6.3 MainThread运行原理

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章来做更详尽的分析。

相关文章
|
6月前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
【7月更文挑战第28天】在Android开发中,确保UI流畅性至关重要。多线程与异步编程技术可将耗时操作移至后台,避免阻塞主线程。我们通常采用`Thread`类、`Handler`与`Looper`、`AsyncTask`及`ExecutorService`等进行多线程编程。
67 2
|
3月前
|
算法 数据处理 Android开发
掌握安卓性能优化的秘诀:电池寿命与运行效率的提升
【10月更文挑战第6天】 本文深入探讨了安卓应用开发中的性能优化技巧,重点分析了影响电池寿命和运行效率的关键因素,并提供了针对性的优化策略。通过代码优化、资源管理、后台任务处理等方法,开发者可以显著提升应用的续航能力和流畅度。同时,结合具体案例,展示了如何在实际开发中应用这些技巧,确保应用在各种场景下都能保持高效运行。本文旨在为安卓开发者提供实用的性能优化指导,助力其打造更优质的应用体验。
85 2
|
7月前
|
Java API 开发工具
如何将python应用编译到android运行
【6月更文挑战第27天】本文介绍在Ubuntu 20上搭建Android开发环境,包括安装JRE/JDK,设置环境变量,添加i386架构,安装依赖和编译工具。并通过`p4a`命令行工具进行apk构建和清理。
131 6
如何将python应用编译到android运行
|
1月前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
2月前
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
6月前
|
Java Android开发
Android面试题经典之Glide取消加载以及线程池优化
Glide通过生命周期管理在`onStop`时暂停请求,`onDestroy`时取消请求,减少资源浪费。在`EngineJob`和`DecodeJob`中使用`cancel`方法标记任务并中断数据获取。当网络请求被取消时,`HttpUrlFetcher`的`cancel`方法设置标志,之后的数据获取会返回`null`,中断加载流程。Glide还使用定制的线程池,如AnimationExecutor、diskCacheExecutor、sourceExecutor和newUnlimitedSourceExecutor,其中某些禁止网络访问,并根据CPU核心数动态调整线程数。
181 2
|
3月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
82 4
|
4月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
165 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
3月前
|
Java Unix Linux
Android Studio中Terminal运行./gradlew clean build提示错误信息
遇到 `./gradlew clean build`命令执行出错时,首先应检查错误信息的具体内容,这通常会指向问题的根源。从权限、环境配置、依赖下载、版本兼容性到项目配置本身,逐一排查并应用相应的解决措施。记住,保持耐心,逐步解决问题,往往复杂问题都是由简单原因引起的。
444 2
|
4月前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
158 5

热门文章

最新文章