Android线程消息通信(二)
创建线程消息队列
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线程消息通信(二)的更多相关文章
- Android线程消息通信(一)
Android在Java标准线程模型的基础上,提供了消息驱动机制,用于多线程之间的通信.基于消息驱动机制的线程通信模型陈伟线程消息通信.在标准线程模型中,线程执行完毕后便退出,而Android扩展了线 ...
- Android线程管理(二)——ActivityThread
线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...
- Android线程间通信机制——深入理解 Looper、Handler、Message
在Android中,经常使用Handler来实现线程间通信,必然要理解Looper , Handler , Message和MessageQueue的使用和原理,下面说一下Looper , Handl ...
- Android线程间通信更新UI的方法(重点分析EventBus)
Android的UI更新只能在UI线程中,即主线程.子线程中如果要进行UI更新,都是要通知主线程来进行. 几种实现方式总结如下,欢迎补充. 1.runOnUiThread() 子线程中持有当前Acti ...
- Python并发编程之线程消息通信机制任务协调(四)
大家好,并发编程 进入第四篇. 本文目录 前言 Event事件 Condition Queue队列 总结 . 前言 前面我已经向大家介绍了,如何使用创建线程,启动线程.相信大家都会有这样一个想法,线程 ...
- Android中Service通信(二)——绑定Service进行通信
一.把输入文本的数据同步到服务的实例(如何执行服务的内部代码) 绑定服务比启动服务更加方便高效,绑定服务中的直接方法调用比Intent作为载体传输更为快捷得多. 1.activity_main.xml ...
- Android线程池(二)
本篇主要介绍Android自带的线程池的管理. 包含开始任务.重新加载.添加删除任务等,示例代码如下: package com.jiao.threadpooltest; import java.uti ...
- android 线程间通信
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 1,共享内存 变量 2,文件,数据库 3,处理器 消息机制 4, 线程 的 等待,通知 ...
- Android线程间通信的几种实现方式
1. 通过Handler机制: private void one() { handler=new Handler(){ @Override public void handleMessage(Mess ...
随机推荐
- 转载:监控每个节点(jvm部分)
操作系统和进程部分 操作系统和进程部分的含义是很清楚的,这里不会描述的很详细.他们列出了基本的资源统计,例如CPU和负载.操作系统部分描述了整个操作系统的情况,进程部分只是描述了Elasticsear ...
- TFS使用指南
上一篇文章已经简略介绍过TFS的安装与管理,本篇文章主要描述一下我个人在工作过程中使用TFS的一些指南与建议.本章内容预览: 1. 项目计划与跟踪 经常有很多朋友在日常聊天中抱怨做计划很无畏,因为计 ...
- java数据结构和算法------顺序查找
package iYou.neugle.search; public class Sequence_search { public static int SequenceSearch(double[] ...
- ios 唯一标示符
大家知道苹果每部 iOS 设备都有一个 UDID,它就像设备的身份证一样,记录着设备的名称.类型甚至一些关于用户的私人信息.通常情况下,UDID 的一个最大功能就是帮助广告发布商向特定用户推送定向广告 ...
- Java学习之IO流总结
---恢复内容开始--- 流是用来读写数据的,java有一个类叫File,它封装的是文件的文件名,只是内存里面的一个对象,真正的文件是在硬盘上的一块区间,在这个文件里面存放着各种各样的数据,我们想读文 ...
- c编程之排序
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 typedef struct Nod ...
- Taxes
Taxes can be one of the largest cash outflows that a firm experiences.The size of the tax bill(税单) i ...
- 前端之JavaScript第四天学习(10)-JavaScript-运算符
运算符 = 用于赋值. 运算符 + 用于加值. 运算符 = 用于给 JavaScript 变量赋值. 算术运算符 + 用于把值加起来. y=5; z=2; x=y+z; 在以上语句执行后,x 的值是 ...
- 【Java】Eclipse导出jar包与javadoc
1.导出jar包 2.导出javadoc 3.jar包添加javadoc 4.出错解决 参考资料: http://www.cnblogs.com/cyh123/p/3345889.html http: ...
- 【ContestHunter】【弱省胡策】【Round3】(C)
容斥原理+Fib Orz HE的神犇们 蒟蒻只能改出来第三题……实在太弱 官方题解:http://pan.baidu.com/s/1o6MdtQq fib的神奇性质……还有解密a[i]的过程……这里就 ...