系列文章解读&说明:
Android系统核心机制基础 的 分析主要分为以下部分:
(01)智能指针wp & sp
(02)Thread类解析
(03)Thread同步机制
(04)handler message机制 java
(05)handler使用案例(Java)
(06)handler message机制 Native
(07)AsyncChannel机制浅析
(08)JNI 基础
(09)Android 添加新模块
本模块分享的内容:handler message机制 java
本章关键点总结 & 说明:
这里关注➕ handler message机制,专注于java层分析,针对3个关键类Looper、MessageQueue、Handler进行分析和解读。
Looper && Handler && MessageQueue分析(Java)
对应用程序而言,Android系统中Java的和其他系统上的相同,是靠消息驱动来工作的,它们大致的工作原理如下:
- 有一个消息队列,可以往这个消息队列中添加或者删除消息。
- 有一个消息循环,不断从消息队列中取出消息,然后处理。
这个工作过程如下所示:
事件源把待处理的消息加入到消息队列,一般是加至队列尾,一些优先级高的消息也可以加至队列头。
- 事件源提交的消息来源:按键、触摸屏、系统或应用程序本身发出的请求消息。
- 处理线程不断从消息队列头中取出消息并处理,事件源可以把优先级高的消息放到队列头,这样优先级高的消息就会首先被处理。
在Android系统中,这些工作主要由Looper和Handler来实现:
- Looper类,用于封装消息循环,并且有一个消息队列。
- Handler类,辅助类,封装了消息处理的各种方法。
1 Looper类分析
下面是一个使用Looper的常见实例:
/定义一个LooperThread
class LooperThread extends Thread {
publicHandler mHandler;
public void run() {
Looper.prepare(); //1 调用prepare
//...
Looper.loop();//2 进入消息循环
}
}
//应用程序使用LooperThread
{
//...
newLooperThread().start();//启动新线程,线程函数是run
}
关键点有2个一个是prepare,一个是loop循环。
1.1 prepare分析
代码分析如下:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//Looper只能调用一次prepare,再次调用会抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//构造一个Looper对象,设置到调用线程的局部变量中
//...
}
TLV(Thread Local Variable)是Java中的线程局部变量类。该类有两个关键函数:
- set:设置调用线程的局部变量。
- get:获取调用线程的局部变量。
注意:set/get的结果都和调用这个函数的线程有关。
prepare会在调用线程的局部变量中设置一个Looper对象。这个调用线程就是LooperThread的run线程。继续看Looper对象的构造,其代码如下所示:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //构造一个消息队列
mThread = Thread.currentThread(); //得到当前线程的Thread对象
}
prepare函数的作用:在调用prepare的线程中,通过ThreadLocal机制,把Looper和调用线程sThreadLocal关联在一起。
1.2 loop循环
代码分析如下:
public static void loop() {
final Looper me = myLooper();//myLooper返回保存在调用线程TLV中的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //取出这个Looper的消息队列
// 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对象中有一个target,它是Handler类型,如果target为空,则表示需要退出消息循环
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//调用该消息的Handler,交给它的dispatchMessage函数处理
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
这里,myLooper的实现如下所示:
public static Looper myLooper() {
return sThreadLocal.get();
}
即仅仅是通过TLV来获取之前在prepare中通过TLV设置的那个Looper,这里总结下Looper的作用:
- Looper封装了一个消息队列。
- Looper的prepare函数把这个Looper和调用prepare的线程(也就是最终的处理线程)绑定在一起了。
- 处理线程调用loop函数,处理来自该消息队列的消息。
当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了。那么该消息就由和Looper绑定的处理线程来处理。
1.3 Looper、Message、Handler之间的关系
- Looper中有一个Message队列,里边存储的待处理的Message对象。
- 每个单独的Message中有一个Handler,这个Handler是用来处理Message的。
2 Handler分析
2.1 Handler关键成员 & 构造函数
Handler的关键成员如下所示:
final MessageQueue mQueue;//一个MessageQueue
final Looper mLooper; //一个Looper
final Callback mCallback; //一个回调用的类
final boolean mAsynchronous; //是否同步
Handler一共有四个构造函数,它们主要的区别,是在对上面三个重要成员变量的初始化上。代码如下:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
//后面两个才是关键的初始化构造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;//得到Looper的消息队列
mCallback = callback;
mAsynchronous = async;
}
在上述构造函数中,Handler中的消息队列变量最终都会指向了Looper的消息队列,这样做的意义:
特殊说明:为了更方便的往Looper的消息队列插入消息,假设不使用Handler,这里有一个很原始的方法:
- 调用Looper的myQueue,它将返回消息队列对象MessageQueue。
- 构造一个Message,填充它的成员,尤其是target变量。
- 调用MessageQueue的enqueueMessage,将消息插入消息队列。
这种原始方法的确很麻烦,且极容易出错。但使用Handler后就变得简单。Handler是以辅助类形式来帮助我们简化编程工作。
2.2 handler与Message的关系
分析handler与Message之间的关系,Handle提供了一系列函数,帮助我们完成创建消息和插入消息队列的工作。如下所示:
final boolean hasMessages(int what) //查看消息队列中是否有消息码是what的消息
final Message obtainMessage(int what) //从Handler中创建一个消息码是what的消息
final void removeMessages(int what) //从消息队列中移除消息码是what的消息
final boolean sendEmptyMessage(int what) //发送一个只填充了消息码的消息
final boolean sendMessage(Message msg) //发送一个消息,该消息添加到队列尾
final boolean sendMessageAtFrontOfQueue(Message msg) //发送一个消息,该消息添加到队列头,所以优先级很高
这里以sendMessage为例进行详细分析,分析sendMessage,代码如下:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
继续分析,调用sendMessageDelayed,代码如下:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
//delayMillis是以当前时间为参照的相对时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
继续分析,调用sendMessageAtTime,代码如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//这里处理的是绝对时间
return enqueueMessage(queue, msg, uptimeMillis);
}
继续分析,调用enqueueMessage,代码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //将msg的target设置成自己。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
若没有Handler辅助,当我们自己操作MessageQueue的enqueueMessage时会很麻烦。Handler把Message的target设为自己的原因:Handler除了封装消息添加等功能外,还封装了消息处理的接口,即handleMessage。
2.3 Handler的消息处理
往Looper的消息队列中加入一个消息,按Looper的处理规则:获取消息后会调用target的dispatchMessage函数,把这个消息派发给Handler处理。此时,target就是handler本身,即还是handler自己来处理消息,处理代码如下所示:
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //如果Message本身有callback,则直接交给Message的callback处理
handleCallback(msg);
} else {
if (mCallback != null) {//如果本Handler设置了mCallback,则交给mCallback处理
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //如果均不合适,最后才是交给子类处理
}
}
这里总结如下:dispatchMessage定义了一套消息处理的优先级,它们分别是:
- Message如果自带了callback处理,则交给callback处理。
- Handler如果设置了全局的mCallback,则交给mCallback处理。
- 如果上述都没有,该消息则会被交给Handler子类实现的handleMessage来处理。当然,这需要从Handler派生并重载handleMessage函数。
在通常情况下,我们一般都是采用第三种方法,即在子类中通过重载handleMessage来完成处理工作的。
3 HandlerThread介绍
3.1 Looper和Handler的同步关系
@1 这里先看一个实例,主要是创建和使用带有消息队列的线程:
//先定义一个LooperThread类
class LooperThread extends Thread {
public Looper myLooper = null;//定义一个public的成员myLooper,初值为空。
public void run() { //假设run在线程2中执行
Looper.prepare();// myLooper必须在这个线程中赋值
myLooper = Looper.myLooper();
Looper.loop();
}
}
@2 下面这段代码在线程1中执行,这里会创建和使用一个带有消息队列机制的线程2
{
//当前已经是一个主线程,即线程1
LooperThread lpThread= new LooperThread;
lpThread.start();//start后会创建线程2
Looper looper = lpThread.myLooper;//<======注意
Handler thread2Handler = new Handler(looper); // thread2Handler和线程2的Looper挂上钩
threadHandler.sendMessage(...); //sendMessage发送的消息将由线程2处理
}
@3 分析,流程说明
- 线程1中创建线程2,并且线程2通过Looper处理消息。
- 线程1中得到线程2的Looper,并且根据这个Looper创建一个Handler,这样发送给该Handler的消息将由线程2处理。
上面的代码是有问题的。标有“注意”的那行代码存在着严重问题的。myLooper的创建是在线程2中,而looper的赋值则在线程1,很有可能此时线程2的run函数还没来得及给myLooper赋值,这样线程1中的looper将取到myLooper的 初值,也就是looper等于null。另外:
Handler thread2Handler = new Handler(looper); 不能替换成
Handler thread2Handler = new Handler(Looper.myLooper());
这是因为,myLooper返回的是调用线程的Looper,即Thread1的Looper,而不是我们想要的Thread2的Looper。
对这个问题,可以采用同步的方式进行处理。这里Android就替我们想好了,它提供了一个HandlerThread来解决这个问题。
3.2 HandlerThread
HandlerThread,它的存在是为了降低创建和使用带有消息队列的线程的难度。它完美地解决了myLooper可能为空的问题。关键代码实现如下:
//线程1调用getLooper来获得线程2的Looper
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();//如果线程2还未创建Looper,则等待
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
线程2运行它的run函数,looper就是在run线程里创建的。代码如下:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();//通知取Looper的线程1,此时Looper已经创建好了
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
wait/ notifyAll就解决了我们的难题。为了避免重复发明*,我们还是多用HandlerThread类。