创建线程消息队列

Android应用程序的消息队列是使用一个MessageQueue对象来描述的,它可以通过调用Looper类的静态成员函数prepareMainLooper或者prepare来创建,其中,前者用来为应用程序的主线程创建消息队列;而后者用来为应用程序的其它子线程创建消息队列。

在分析Android应用程序线程的消息队列的创建过程之前,先要了解一下Looper类和MessageQueue类的实现。

Looper类源代码:

public final class Looper {
private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue;
final Thread mThread;
volatile boolean mRun; private Printer mLogging; /** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
} private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
} /** Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
} /**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity(); for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
} // This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
} msg.target.dispatchMessage(msg); if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
} // Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
} msg.recycle();
}
} /**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
} /**
* Control logging of messages as they are processed by this Looper. If
* enabled, a log message will be written to <var>printer</var>
* at the beginning and ending of each message dispatch, identifying the
* target Handler and message contents.
*
* @param printer A Printer object that will receive log messages, or
* null to disable message logging.
*/
public void setMessageLogging(Printer printer) {
mLogging = printer;
} /**
* Return the {@link MessageQueue} object associated with the current
* thread. This must be called from a thread running a Looper, or a
* NullPointerException will be thrown.
*/
public static MessageQueue myQueue() {
return myLooper().mQueue;
} private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
......
}

Looper类有一个类型为ThreadLocal的静态成员变量sThreadLocal,它是用来保存线程中的Looper对象的。我们可以将Looper类的静态成员变量sThread理解为一个线程局部变量,或者一个HashMap,每一个创建了消息队列的Android应用程序线程在里面都有一个关联的Looper对象。我们调用这个静态成员变量的成员函数get或者;当我们调用这个静态成员变量的成员函数set时,就可以将一个Looper对象与当前线程关联起来。

Looper类的静态成员函数prepare中,if语句检查当前线程是否已经建有一个Looper对象了。如果有,那么就会抛出一个异常;否则,创建一个Looper对象,然后将这个Looper对象保存在Looper类的静态成员变量sThreadLocal中。总之,prepare方法创建了一个线程独立且唯一的Looper对象,如果要访问这Looper对象,只需要调用myLooper方法。

Looper类的静态成员函数prepareMainLooper中,先调用Looper类的静态成员函数prepare在当前线程中创建一个Looper对象,然后调用Looper的静态成员函数setMainLooper将这个Looper对象保存在Looper类的静态成员变量mMainLooper中。

Looper类的静态成员函数prepareMainLooper只能在Android应用程序的主线程中调用。Android应用程序主线程是一个特殊的线程,只有它才能执行与UI相关的操作,因此,我们又将它称作UI线程。将Android应用程序的主线程Looper对象保存在一个独立的静态成员变量中,是为了让其它线程可以通过Looper类的静态成员函数getMainLooper来访问它,从而可以往它的消息队列中发送一些与UI相关的消息。

一个Looper对象在创建过程中,会在内部创建一个MessageQueue对象,并且保存在它的成员变量mQueue中。

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;  //表示当前线程处于run状态
mThread = Thread.currentThread();  //存储当前线程
}

一个MessageQueue对象在创建的过程中,又会在C++层中创建一个NativeMessageQueue对象,这是通过调用MessageQueue类的成员函数nativeInit来实现的。

public final class MessageQueue {
// True if the message queue can be quit.
private final boolean mQuitAllowed; @SuppressWarnings("unused")
private int mPtr; // used by native code
//... ...
private native static int nativeInit();
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
}

在MessageQueue构造方法中,首先给传入的quitAllowed参数的成员变量mQuitAllowed赋值为true,表示当前消息循环允许退出;然后调用Native方法nativeInit进入Native层的初始化。

nativeInit方法由JNI层的android_os_MessageQueue_nativeInit实现,其主要工作分为三部分:

1)创建一个NativeMessageQueue类型的对象,并增加其引用;

2)NativeMessageQueue对象在创建过程中,会在其内部创建一个C++层的Looper对象。C++层的Looper对象在创建的过程中,又会在内部创建一个管道,这个管道的读端和写端分别为mWakeReadPipeFd和mWakeWritePipeFd。这个管道在一个线程的消息循环过程起到的作用非常大。首先,当一个线程没有新的消息要处理时,它就会睡眠在这个管道的读端文件描述符上,直到有新的消息需要处理为止;其次,当其它线程向这个线程的消息队列发送了一个消息之后,其它线程就会通过这个管道的写端文件描述符往这个管道写入一个数据,从而将这个线程唤醒,以便它可以对刚刚发送到它的消息队列中的消息进行处理;

3)将NativeMessageQueue与Java层的MessageQueue关联起来。Java层MessageQueue对象的mPtr成员变量保存的是JNI层nativeMessageQueue的地址,可以通过mPtr成员变量访问Native层的NativeMessageQueue对象。

Looper类、MessageQueue类、Looper(Native)类和NativeMessage类的关系图示

下一篇文章将详细介绍线程消息循环过程

Android线程消息通信(二)的更多相关文章

  1. Android线程消息通信(一)

    Android在Java标准线程模型的基础上,提供了消息驱动机制,用于多线程之间的通信.基于消息驱动机制的线程通信模型陈伟线程消息通信.在标准线程模型中,线程执行完毕后便退出,而Android扩展了线 ...

  2. Android线程管理(二)——ActivityThread

    线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...

  3. Android线程间通信机制——深入理解 Looper、Handler、Message

    在Android中,经常使用Handler来实现线程间通信,必然要理解Looper , Handler , Message和MessageQueue的使用和原理,下面说一下Looper , Handl ...

  4. Android线程间通信更新UI的方法(重点分析EventBus)

    Android的UI更新只能在UI线程中,即主线程.子线程中如果要进行UI更新,都是要通知主线程来进行. 几种实现方式总结如下,欢迎补充. 1.runOnUiThread() 子线程中持有当前Acti ...

  5. Python并发编程之线程消息通信机制任务协调(四)

    大家好,并发编程 进入第四篇. 本文目录 前言 Event事件 Condition Queue队列 总结 . 前言 前面我已经向大家介绍了,如何使用创建线程,启动线程.相信大家都会有这样一个想法,线程 ...

  6. Android中Service通信(二)——绑定Service进行通信

    一.把输入文本的数据同步到服务的实例(如何执行服务的内部代码) 绑定服务比启动服务更加方便高效,绑定服务中的直接方法调用比Intent作为载体传输更为快捷得多. 1.activity_main.xml ...

  7. Android线程池(二)

    本篇主要介绍Android自带的线程池的管理. 包含开始任务.重新加载.添加删除任务等,示例代码如下: package com.jiao.threadpooltest; import java.uti ...

  8. android 线程间通信

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 1,共享内存 变量 2,文件,数据库 3,处理器 消息机制 4, 线程 的 等待,通知 ...

  9. Android线程间通信的几种实现方式

    1. 通过Handler机制: private void one() { handler=new Handler(){ @Override public void handleMessage(Mess ...

随机推荐

  1. 关于HTML中,绝对定位,相对定位的理解...(学习HTML过程中的小记录)

    关于HTML中,绝对定位,相对定位的理解...(学习HTML过程中的小记录)   作者:王可利(Star·星星) HTML中 相对定位:position:relative; 绝对定位:position ...

  2. [转]ORACLE的ProC用法讲解

    pro*c是高级的用法,OCI是oracle的基础用法 如何编译.pc文件: proc code=cpp  parse=none iname=filename.pc oname=filename.cp ...

  3. hdu 5166 Missing number

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5166 Missing number Description There is a permutatio ...

  4. OpenStack: OVS安装

    > OVS安装:1. Install the Open vSwitch plug-in and its dependencies:# apt-get install \neutron-plugi ...

  5. iOS学习之UIView

    一.UI编程概述      1.UI的本意是用户界面,是英文User和Interface的缩写.      2.UI设计则是指对软件的人机交互.操作逻辑.界面美观的整体设计.      3.软件设计可 ...

  6. Android编程: Activity生命周期和LogCat使用

    学习内容:Activity生命周期和LogCat使用 ====Activity生命周期==== 图示(转载): 创建    onCreate重启    onRestart开始    onStart恢复 ...

  7. Filter介绍

    Filter可人为是Servlet的一种“加强版”,它重要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链.使用Filter的完整的流程是:Filt ...

  8. Bootstrap入门二:响应式页面布局

    Bootstrap 提供了一套响应式.移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列.它包含了易于使用的预定义类,还有强大的mixin 用于生成更具 ...

  9. SharedPreferences实现记住密码功能

    SharedPerferences 简单介绍 用于保存简单的键值对数据: 它将数据放在 /data/data/<package name>/shared_prefs目录下,用xml文件保存 ...

  10. 《JavaScript高级程序设计》第4章 变量、作用域和内存问题

    4.1 基本类型和引用类型 5种基本类型:Undefined.Null.Boolean.Number和String,这5种基本类型是按值访问的,因为可以操作保存在变量中的实际的值. 引用类型:可能由多 ...