创建线程消息队列

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. ED/EP系列7《指令速查表》

    命 令                                                             CLA                  INS             ...

  2. 关于Haproxy安装和配置:负载配置【haproxy.cfg】问题记录

    1.  存放地址: more /etc/haproxy/haproxy.cfg ps -ef | grep haproxy 看看有没有haproxy的进程就是了 或者看看服务器的23306的端口有没有 ...

  3. 调用微信退款接口时出现System.Security.Cryptography.CryptographicException: 出现了内部错误 解决办法

    我总结了一下出现证书无法加载的原因有以下三个 1.证书密码不正确,微信证书密码就是商户号 解决办法:请检查证书密码是不是和商户号一致 2.IIS设置错误,未加载用户配置文件 解决办法:找到网站使用的应 ...

  4. python 实现求和、计数、最大最小值、平均值、中位数、标准偏差、百分比。

    import sys class Stats: def __init__(self, sequence): # sequence of numbers we will process # conver ...

  5. b75,gtx560,I5 安装10.10.2

    1.安装变色龙,wowpc.iso,这个是可以让电脑从windows引导 mac 安装的. 2.把黑苹果CDR压到一个硬盘分区里去. 3.安装10.10.2,把安装盘里的extra拷贝到 系统盘里 , ...

  6. MongoDB的交互(mongodb/node-mongodb-native)、MongoDB入门

    MongoDB 开源,高性能的NoSQL数据库:支持索引.集群.复制和故障转移.各种语言的驱动程序:高伸缩性: NoSQL毕竟还处于发展阶段,也有说它的各种问题的:http://coolshell.c ...

  7. 兼容sdk7&iOS7的issue解决小片段总结

    ios7新增加的icon尺寸: 76 x 76:Size for iPad 2 and iPad mini (standard resolution) 120 x 120 :Size for iPho ...

  8. mac os x 系统安装 genymotion android 模拟器

    如果你有 apk 文件 想 运行一下看看 ,但是又没有  android 设备 ,那么 genymotion 将会是一个  很好的解决方案. 1.安装 下载链接: https://cloud.geny ...

  9. 31.DDR2问题3_waring?

    Warning: (vlog-2275) 'ddr2_controller' already exists and will be overwritte. 出现这个waring,是因为xxx_bb.v ...

  10. Python实现NN(神经网络)

    Python实现NN(神经网络) 参考自Github开源代码:https://github.com/dennybritz/nn-from-scratch 运行环境 Pyhton3 numpy(科学计算 ...