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 ...
随机推荐
- 使用MSYS2编译64位gvim
1. 下载安装MSYS2 在https://msys2.github.io/下载MSYS2,推荐下载x86-64版,此版本内置了MinGW32与MinGW64 安装后首先更新MSYS2系统,顺序执行下 ...
- ED/EP系列6《扩展应用》
包括:电子钱包复合应用:电子钱包灰锁应用. 1. 复合应用模式 Ø INITIALIZE FOR CAPP PURCHASE(复合应用消费初始化): Ø UPDATE CAPP DATA CACHE( ...
- C# 堆和栈的区别-该文转自:http://www.itcodes.cn/746.html | 程序人生
理解堆与栈对于理解.NET中的内存管理.垃圾回收.错误和异常.调试与日志有很大的帮助.垃圾回收的机制使程序员从复杂的内存管理中解脱出来,虽然绝大多数的C#程序并不需要程序员手动管理内存,但这并不代表程 ...
- android开发系列之gradle认识
后面的系列博客,我将会写一写自己这段时间对于android的学习.认识.体会,希望能够与大家分享. 相信大家从ADT开发切换到android studio最大.最直观的变化就是gradle,因为在an ...
- Asp.Net MVC如何返回401响应码
需求: 在默认创建的Asp.Net MVC项目中(这里使用VS2013),需要手动返回一个401响应码给浏览器.我们的代码可能是下面这样子的. public ActionResult Un ...
- func_num_args(),func_get_arg(),func_get_args()
<?php function testFunction1(){ return func_num_args(); } function testFunction2(){ return func_g ...
- PHP自定义函数使用外部变量
一般,php的自定义函数不能直接使用外部变量. 在php自定义函数中使用外部变量前,需要先使用global对外部变量进行声明. <?php $var = "hello World!& ...
- VisualSVN Server添加svn项目
如果你想把一个项目添加到svn的步骤: 第一步:打开VisualSVN Server,在目录Repositries目录下,创建一个文件夹名称: 第二步:要把导入的项目,指定到这个路径:如下图:
- StatusBar & StatusBarItem
StatusBar & StatusBarItem StatusBar StatusBar class StatusBarItem StatusBarItem class Example &l ...
- VS 与 SQLite数据库 连接
SQLite并没有一次性做到位,只有下载这些东西是不能放在vs2010中并马上使用的,下载下来的文件中有sqlite3.c/h/dll/def,还是不够用的.我们需要的sqlite3.lib文件并不在 ...