Chromium on Android: Android在系统Chromium为了实现主消息循环分析

时间:2022-09-11 13:02:25

总结:刚开始接触一个Chromium on Android时间。很好奇Chromium主消息循环是如何整合Android应用。

为Android计划,一旦启动,主线程将具有Java消息层循环处理系统事件,如用户输入事件,而Chromium为,己还有一套消息循环的实现,这个实现有哪些特点。又将怎样无缝整合到Android Java层的消息循环中去,正是本文所要讨论的话题。

原创文章系列。转载请注明原始出处http://blog.csdn.net/hongbomin/article/details/41258469.

消息循环和主消息循环

消息循环,或叫做事件循环。是异步编程模型中一个重要的概念,用来处理单个线程中发生的异步事件。这些异步事件包含用户输入事件,系统事件。Timer以及线程间派发的异步任务等。

Chromium系统将消息循环抽象为MessageLoop类,规定每一个线程最多仅仅能同一时候执行一个MessageLoop实例,MessageLoop提供了PostTask系列方法同意向任务队列中加入新的异步任务。当MessageLoop发现有新任务到达,它都会从轮询自己的任务队列并从按“先进先出”的方式执行任务,周而复始,直到收到MessageLoop::Quit消息。消息循环才会退出。

Chromium依照所需处理的异步事件,将MessageLoop划分为几种不同的类型:

  • TYPE_DEFAULT: 默认的消息循环。仅仅能处理定时器Timer和异步任务。
  • TYPE_UI:不但能够处理定时器Timer和异步任务,还能够处理系统UI事件。主线程使用的就是该类型的MessageLoop。也就是主消息循环;
  • TYPE_IO: 支持异步IO事件,Chromium全部处理IPC消息的IO线程创建的都是该类型的MessageLoop;
  • TYPE_JAVA: 专为Android平台设计的消息循环类型,后端实现是Java层的消息处理器。用来运行加入到MessageLoop中的任务。其行为与TYPE_UI类型差点儿相同,但创建时不能使用主线程上的MessagePump工厂方法。

    注:该类型的MessageLoop与本文讨论的消息循环无关。

MessageLoop详细实现和平台相关,即使在同样的平台上,因为使用了不同的事件处理库。事实上现方式也有可能不同。

Chromium将平台相关的实现封装在MessagePump抽象类中,类MessageLoop和MessagePump之间的关系例如以下所看到的:

Chromium on Android: Android在系统Chromium为了实现主消息循环分析

MessagePump的详细实现提供了平台相关的异步事件处理。而MessageLoop提供轮询和调度异步任务的基本框架,两者通过MessagePump::Delegate抽象接口关联起来。

MessagePump::Run的基本代码骨架例如以下:

for (;;) {
bool did_work =DoInternalWork();
if (should_quit_)
break; did_work |= delegate_->DoWork();
if (should_quit_)
break; TimeTicks next_time;
did_work |=delegate_->DoDelayedWork(&next_time);
if (should_quit_)
break; if (did_work)
continue; did_work =delegate_->DoIdleWork();
if (should_quit_)
break; if (did_work)
continue; WaitForWork();
}

上述代码片段中,有三点须要特别说明:

  • MessagePump既要负责响应系统的异步事件,又要给足够的时间片段调度Delegate去运行异步任务。所以MessagePump是以混合交错的方式调用DoInternalWork,DoWork, DoDelayedWork和DoIdleWork,以保证不论什么一类任务不会因得不到运行而都发生“饥饿”现象;
  • DoInternalWork和WaitForWork都是MessagePump详细实现的私有方法。DoInternalWork负责分发下一个UI事件或者是通知下一个IO完毕事件,而WaitForWork会堵塞MessagePump::Run方法直到当前有任务须要运行;
  • 每一个MessagePump都有设置should_quit_标记位。一旦MessagePump::Quit被调用了。should_quit_将置为true, 每次MessagePump处理完一类任务时都要去检查should_quit_标记。以决定是否继续处理兴许的任务。当发现should_quit_为true时。直接跳出for循环体。

嵌套的消息循环(Nested MessageLoop)

假设把消息循环比作是一个须要做非常多事情的梦境。那么嵌套的消息循环就是“盗梦空间”了,从一个梦境进入另外一个梦境了。

简单地说,嵌套的消息循环就是当前消息循环中因运行某个任务而进入另外一个消息循环,而且当前的消息循环*等待嵌套消息循环退出,才干继续运行后面的任务,比如。当弹出MessageBox对话框时。意味着进入了一个新的消息循环,直到MessageBox的OK或者Cancelbutton按下之后这个消息循环才干退出。

RunLoop是Chromium在后期代码重构时引入的类,主要是为了开发人员更加方便地使用嵌套的消息循环,每一个RunLoop都有一个run_depth值,表示嵌套的层数,仅仅有当run_depth值大于1时才说明这个RunLoop被嵌套了。RunLoop是个比較特殊的对象,它在栈上创建,当MessageLoop::Run函数运行完成。RunLoop自己主动释放掉:

void MessageLoop::Run() {
RunLoop run_loop;
run_loop.Run();
}

每一个MessageLoop中都有一个指针。指向当前正在执行的RunLoop实例。调用RunLoop::Run方法时,MessageLoop::current()消息循环中的RunLoop指针也会随之改动为当前执行的RunLoop。换句话说,假设此时调用MessageLoop::current()->PostTask,那么将在嵌套的消息循环中执行异步任务。

嵌套消息循环基本用法例如以下代码所看到的:

void RunNestedLoop(RunLoop* run_loop) {
MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
run_loop->Run();
}
} // 在栈上创建一个新的RunLoop
RunLoop nested_run_loop; // 在当前消息循环中异步启动一个嵌套的消息循环;
MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&RunNestedLoop,Unretained(&nested_run_loop)));
// 一旦RunNestedLoop运行,MessageLoop::current()内部的RunLoop指针会指向nested_run_loop, PostTask将在嵌套RunLoop上运行Quit操作
MessageLoop::current()->PostTask(FROM_HERE, nested_run_loop.QuitClosure());
// 异步运行task_callback时,嵌套RunLoop此时已经退出了,恢复到原先的消息队列中运行;
MessageLoop::current()->PostTask(FROM_HERE,task_callback);

Android系统的消息循环机制

Android平台上消息循环机制的基本原理与Chromium系统中很相似,不同的是,Android消息循环的抽象是在Java层构建的。

Android系统中,android.os.Looperandroid.os.Handler是消息循环机制中两个很重要的类。

Looper类似于Chromium的MessageLoop(或者是RunLoop)概念。Android系统中每一个线程都能够关联一个Looper,用于处理异步消息或者Runable对象等。Android系统线程默认是没有关联不论什么一个Looper,开发人员能够显式调用Looper.prepare和Looper.loop为线程创建并执行Looper,直到退出Looper。Looper之间的交互则须要通过类Handler完毕。

Handler同意开发人员向线程的消息队列发送或者处理消息和Runable对象,它有两个用途:1)通过handleMessage方法异步处理自己线程中的消息队列;2)通过sendMessage或post方法系列向其它线程的消息队列发送消息。

一个线程能够有多个Handler,Looper轮询消息队列时,负责将消息派发给目标Handler,由目标Handler的handleMessage来处理这个消息。

Looper和Handler之间的关系例如以下图所看到的(图片来源于这里):

Chromium on Android: Android在系统Chromium为了实现主消息循环分析

Chromium使用Android的消息循环机制

这里所讨论的主要是针对主线程的消息循环。即UI线程,由于IO线程是在native层创建的。并没有涉及到与UI元素的交互事件。而且Android也是POSIX系统。不用考虑IO线程消息循环的整合问题。

如上所述,Android系统的消息循环是构建在Java层的。Chromium须要解决的问题,怎样在主线程上将C++层负责管理和调度异步任务的MessageLoop整合到Android系统的控制路径上。答案当然是使用android.os.Handler。

首先来看看Android平台上MessagePumpForUI详细实现。与其它平台不同的是,MessagePumpForUI的实现中,启动整个消息循环处理逻辑的Run方法竟然不做不论什么事情,取而代之是,新增了一个Start方法在Chromium中启用Android系统的消息循环,例如以下代码所看到的:

void MessagePumpForUI::Run(Delegate* delegate) {
NOTREACHED()<< "UnitTests should rely on MessagePumpForUIStub in test_stub_android.h";
}
void MessagePumpForUI::Start(Delegate* delegate) {
run_loop_ = newRunLoop();
// Since the RunLoopwas just created above, BeforeRun should be guaranteed to
// return true (itonly returns false if the RunLoop has been Quit already).
if(!run_loop_->BeforeRun())
NOTREACHED();
JNIEnv* env =base::android::AttachCurrentThread();
system_message_handler_obj_.Reset(
Java_SystemMessageHandler_create(
env,reinterpret_cast<intptr_t>(delegate)));
}

Start方法会通过JNI向Java层请求创建一个继承于android.os.Handler的SystemMessageHandler,向当前UI线程的Looper添加一个新的Handler类型,它提供了自己的handleMessage方法:

class SystemMessageHandler extends android.os.Handler {
private staticfinal int SCHEDULED_WORK = 1;
private staticfinal int DELAYED_SCHEDULED_WORK = 2;
privateSystemMessageHandler(long messagePumpDelegateNative) {
mMessagePumpDelegateNative = messagePumpDelegateNative;
}
@Override
public voidhandleMessage(Message msg) {
if (msg.what== DELAYED_SCHEDULED_WORK) {
mDelayedScheduledTimeTicks = 0;
}
nativeDoRunLoopOnce(mMessagePumpDelegateNative,mDelayedScheduledTimeTicks);
}

}

那么。Chromium系统C++层是怎样将运行异步任务的请求发生给AndroidJava层的Handler,以及Handler又是怎样去运行在ChromiumC++层的异步任务呢?

C++层将异步任务发送给Java层

每当C++层通过调用MessageLoop::current()->PostTask*向UI线程的MessageLoop加入新的异步任务时。MessageLoop::ScheduleWork都发起调度任务运行的动作,而MessageLoop::ScheduleWork则是请求MessagePump来完毕这个动作。其调用链为:

Chromium on Android: Android在系统Chromium为了实现主消息循环分析

所以MessagePumpForUI的ScheduleWork须要将来自C++层请求发送给Java层的SystemMessageHandler:

void MessagePumpForUI::ScheduleWork() {
JNIEnv* env =base::android::AttachCurrentThread();
Java_SystemMessageHandler_scheduleWork(env,
system_message_handler_obj_.obj());
}

对应地,SystemMessageHandler的scheduleWork实现代码例如以下:

class SystemMessageHandler extends Handler {
...
@CalledByNative
private voidscheduleWork() {
sendEmptyMessage(SCHEDULED_WORK);
}
...
}

不难看出,SystemMessageHandler.scheduleWork唯一要做的事情就是向UI线程的消息队列发送一个类型SCHEDULED_WORK的消息。接下来再来看看Java层是怎样处理这个类型的消息的。

Java层Handler处理来自C++层的异步任务

当UI线程的Looper收到这个SCHEDULED_WORK异步消息后,它会准确无误的派发给SystemMessageHandler,由SystemMessageHandler.handleMessage重载方法去处理这个消息。如上代码所看到的,handleMessage运行了一个定义在MessagePumpForUI中的native方法DoRunLoopOnce。事实上现代码例如以下:

static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate, jlong delayed_scheduled_time_ticks) {
base::MessagePump::Delegate* delegate =
reinterpret_cast<base::MessagePump::Delegate*>(native_delegate);
bool did_work =delegate->DoWork();
base::TimeTicksnext_delayed_work_time;
did_work |=delegate->DoDelayedWork(&next_delayed_work_time); if(!next_delayed_work_time.is_null()) {
if(delayed_scheduled_time_ticks == 0 ||
next_delayed_work_time < base::TimeTicks::FromInternalValue(
delayed_scheduled_time_ticks)) {
Java_SystemMessageHandler_scheduleDelayedWork(env, obj,
next_delayed_work_time.ToInternalValue(),
(next_delayed_work_time -
base::TimeTicks::Now()).InMillisecondsRoundedUp());
}
}
if (did_work)
return; delegate->DoIdleWork();
}

DoRunLoopOnce方法使C++层的MessageLoop有机会去处理自己的异步任务,包含延时任务和Idle任务。

至此,通过MessagePumpForUI和SystemMessageHandler的实现,Chromium系统在主线程上已经能够无缝整合到Android的消息循环中,

小结

Android SDK提供的Handler类为Chromium系统将自己的消息循环无缝整合到Android系统中提供了相当大的便利性。Chromium的MessageLoop通过JNI向Java层的Handler发送异步消息。当Looper派发这个异步消息时,Java层的消息处理器由通过JNI调用native方法请求Chromium的MessageLoop去调用异步任务的运行,从而完毕了Chromium浏览器在主线程上与Android系统消息循环的整合工作。

原创文章系列。转载请注明原始出处http://blog.csdn.net/hongbomin/article/details/41258469.

版权声明:本文博客原创文章。博客,未经同意,不得转载。

Chromium on Android: Android在系统Chromium为了实现主消息循环分析的更多相关文章

  1. Android 4&period;X 系统加载 so 失败的原因分析

    1 so 加载过程 so 加载的过程可以参考小米的系统工程师的文章loadLibrary动态库加载过程分析 2 问题分析 2.1 问题 年前项目里新加了一个 so库,但发现native 方法的找不到的 ...

  2. Android消息循环分析

    我们的经常使用的系统中,程序的工作一般是有事件驱动和消息驱动两种方式,在Android系统中,Java应用程序是靠消息驱动来工作的. 消息驱动的原理就是: 1. 有一个消息队列.能够往这个队列中投递消 ...

  3. Android之MessageQueue、Looper、Handler与消息循环

    在android的activity中有各种各样的事件,而这些事件最终是转换为消息来处理的.android中的消息系统涉及到: *  消息发送 *  消息队列 *  消息循环 *  消息分发 *  消息 ...

  4. Android监听系统短信数据库变化-提取短信内容

    由于监听系统短信广播受到权限的限制,所以很多手机可能使用这种方式没法监听广播,从而没办法获取到系统短信,所以又重新开辟一条路. Android监听系统短信数据库内容变化使用场景: 1.监听短信数据库的 ...

  5. 让Android程序获得系统的权限,实现关机重启,静默安装等功能

    引用:http://www.cnblogs.com/welenwho/archive/2012/05/10/2494984.html android想要获得系统权限有几种途径,一种就是你的程序固化的系 ...

  6. Android manifest之系统自带的permission

    Android manifest之系统自带的permission 本文描述Android系统自带的permission.点击查看:“关于permission的原始定义和说明”.点击查看:“Androi ...

  7. Android应用与系统安全防御

    来源:HTTP://WWW.CNBLOGS.COM/GOODHACKER/P/3864680.HTML ANDROID应用安全防御 Android应用的安全隐患包括三个方面:代码安全.数据安全和组件安 ...

  8. 图解Android - Android GUI 系统 &lpar;1&rpar; - 概论

    Android的GUI系统是Android最重要也最复杂的系统之一.它包括以下部分: 窗口和图形系统 - Window and View Manager System. 显示合成系统 - Surfac ...

  9. 图解Android - Android GUI 系统 &lpar;2&rpar; - 窗口管理 &lpar;View&comma; Canvas&comma; Window Manager&rpar;

    Android 的窗口管理系统 (View, Canvas, WindowManager) 在图解Android - Zygote 和 System Server 启动分析一 文里,我们已经知道And ...

随机推荐

  1. 无法识别的配置节 connectionStrings

    那啥,asp.net不支持connectionStrings.把IIS中的版本换成2.0或以上就OK了.

  2. 解决Visual Studio 2010闪退问题

    许多人都会面临这样的问题,vs2010闪退,明明程序执行成功,明明没有错误,缺闪了一下结束. 闪退问题主要是缓存的问题,通过在程序末尾,即main函数后增加 getchar(); getchar(); ...

  3. JS&sol;jQuery宽高的理解和应用

    1.widows:窗口.window对象可省略 2.document对象是window对象的一部分 浏览器的Html文档成为Document对象 window.location===document. ...

  4. Android开发之bug-No Activity found to handle Intent

    android.content.ActivityNotFoundException: No Activity found to handle Intent 做Android开发中,使用隐式intent ...

  5. &lbrack;HNOI 2014&rsqb;画框

    Description 题库链接 \(T\) 组询问,每组询问给你个 \(2\times N\) 的带权二分图,两个权值 \(a,b\) ,让你做匹配使得 \[\sum a\times \sum b\ ...

  6. featuremap尺寸的计算

    对于卷积层,向下取整 对于池化层:想上取整 output=((input+2*pad-dilation*(kernel-1)+1)/stride)+1 input:输入尺寸 output:输出尺寸 p ...

  7. CS50&period;5

    函数,全局变量,参数,返回值. 1,类型转换. 各种数据类型进行转换 2,API函数 应用程序编程接口. application programming interface 写写随笔吧,先说计算机.. ...

  8. &commat;Component 元注解

    @Component 元注解 这是一个元注解,意思是它可以用于标注其他注解,被它标注的注解和它起到相同或者类似的作用.Spring用它定义了其他具有特定意义的注解如@Controller @Servi ...

  9. 优秀的PHP开发者是怎样炼成的?

    4.在数据库中避免使用联合操作 比起其它的Web编程语言来说,PHP的数据库功能十分强大.但是在PHP中数据库的运行仍然是一件十分费时费力的事情,所以,作为一个Web程序员,要尽量减少数据库的查询操作 ...

  10. 仅用CSS3创建h5预加载交错圈

    <head> <meta charset="UTF-8"> <title></title> <style type=&quot ...