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

总结:刚开始接触一个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之间的关系例如以下所看到的:

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使用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来完毕这个动作。其调用链为:

所以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.

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

时间: 2024-10-05 07:03:59

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

Chromium on Android: Android系统上Chromium主消息循环的实现分析

摘要:刚一开始接触Chromium on Android时,就很好奇Chromium的主消息循环是怎么整合到Android应用程序中的.对于Android程序来说,一旦启动,主线程就会有一个Java层的消息循环处理用户输入事件等系统事件,而对Chromium来说,它有自己另一套消息循环的实现,这个实现有哪些特点,又将如何无缝整合到Android Java层的消息循环中去,正是本文所要讨论的话题. 原创文章系列,转载请注明原始出处为http://blog.csdn.net/hongbomin/ar

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

1 so 加载过程 so 加载的过程可以参考小米的系统工程师的文章loadLibrary动态库加载过程分析 2 问题分析 2.1 问题 年前项目里新加了一个 so库,但发现native 方法的找不到的 crash 好多,好些都是报了java.lang.unsatisfiedlinkerror native method not found,而且基本上是出现在4.x的系统里,特别是 4.4,4.2的系统.在网络上搜索相关的可能导致到这个问题的原因: so 文件没有在对应架构的目录里找到: 方法名有

Android消息循环分析

我们的经常使用的系统中.程序的工作一般是有事件驱动和消息驱动两种方式,在Android系统中,Java应用程序是靠消息驱动来工作的. 消息驱动的原理就是: 1. 有一个消息队列.能够往这个队列中投递消息; 2. 有一个消息循环.不断从消息队列中取出消息.然后进行处理. 在Android中通过Looper来封装消息循环.同一时候在当中封装了一个消息队列MessageQueue. 另外Android给我们提供了一个封装类.来运行消息的投递,消息的处理.即Handler. <!--more--> 在

Android IntentService源码理解 及 HandlerThread构建消息循环机制分析

前言:前面写了Handler的源码理解,关于Handler在我们Android开发中是到处能见到的异步通信方式.那么,在Android原生里,有那些也有到了Handler机制的呢?有很多,比如我们今天所要理解分析的IntentService就使用到了Handler.接下来,我们来深入了解一下. HandlerThread: IntentService使用到了Handler+HandlerThread构建的带有消息循环的异步任务处理机制,我们先简单看一下HandlerThread是如何工作的吧.

Chromium on Android: Android L平台上WebView的变化及其对浏览器厂商的影响分析

摘要:Android L平台在图形渲染方面有一项重要的改进,它引入了一个专门的线程用于执行渲染工作,UI线程负责生成的显示列表(DisplayList),渲染线程负责重放(playback)这个显示列表绘制最终的内容,因此Chromium WebView在图形栈的实现方面也作了相应的调整,以支持Android L系统上新的渲染线程模型.本文将深度分析Chromium WebView的渲染流水线是如何无缝整合到Android L系统的渲染模型中,以及对目前市场主流浏览器厂商将会产生什么样影响等问题

Chromium线程模型、消息循环

多线程的麻烦 多线程编程是一件麻烦的事,相信很多人深有体会.执行顺序的不确定性,资源的并发访问一直困扰着众多程序员.解决多线程编程问题的方法分为两类:一是对并发访问的资源直接加锁:二是避免并发访问资源:Chromium采用第二种思想来设计多线程模型,通过在线程之间传递消息来实现跨进程通讯. 设计原则 Chromium希望尽量保持UI处于响应状态.为此遵循如下设计原则: 1 不在UI线程上执行任何阻塞I/O操作,以及其它耗时操作. 2 少用锁和线程安全对象 3 避免阻塞I/O线程 4 线程之间不要

Android WebView加载Chromium动态库的过程分析

Chromium动态库的体积比较大,有27M左右,其中程序段和数据段分别占据25.65M和1.35M.如果按照通常方式加载Chromium动态库,那么当有N个正在运行的App使用WebView时,系统需要为Chromium动态库分配的内存为(25.65 + N x 1.35)M.这是非常可观的.为此,Android使用了特殊的方式加载Chromium动态库.本文接下来就详细分析这种特殊的加载方式. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 为什么当有

android 4.4上chromium介绍

纠结啊为毛感觉只过了一天,就有两天没写了,今天晚上的机票离开上海.希望小叶子不会难为我. 昨晚研究了一下插卡小哥的业务流程,发现他并不是每个房间都插的,时间在每晚的9点到11点之间.估计是会在周边每个宾馆巡回一遍,插广告的房间有两种可能,第一种就是他们和宾馆沆瀣一气,知道哪个房间有人,第二种可能就是他们只给大床房的房间发,7天没有单间所以单身旅客一般会选择大床房,这绝对是优质潜在用户哇. 晚上看了个 正者无敌 还挺好看的样子,里面男主娶了3房姨太太,不错不错~ 晚上做了个梦,醒来后还是格外清晰,

chromium中的GN构建系统

chromium中的GN构建系统原创云水木石 最后发布于2017-06-23 17:16:35 阅读数 6924 收藏展开阅读最新的chromium源码,发现项目的构建系统已经从GYP全面切换到GN了.在软件开发中,经常有人忠告:不要重复造轮子.但谷歌可不管这个,造的轮子一个接一个,谁叫人家牛呢?chromiumi项目为啥要折腾构建系统呢?因为谷歌chrome浏览器追求一个字:快.不仅浏览器的速度要快,构建系统也要追求快. 构建系统简介在探讨chromium的最新GN构建系统之前,回顾一下软件开