参考博客:Android Handler消息机制原理最全解读(持续补充中)
1.什么是Handler?
Handler是Android向我们提供的一种消息传递机制,来帮助我们将子线程的数据传递给主线程.因为在我们的开发中,耗时的操作是要放在子线程中执行,我们需要将子线程中执行的结果通知到主线程,来去做一些UI的更新.
2.原理及使用.
2.1. 常规使用
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
Toast.makeText(mContext,"收到来自非主线程的消息1",Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(mContext,"收到来自非主线程的消息2",Toast.LENGTH_SHORT).show();
break;
}
}
};
private Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Message message = mHandler.obtainMessage();
message.what = 1;
message.arg1 = 2;
message.obj = "收到来自非主线程的消息1";
mHandler.sendMessage(message);
}
});
① 主线程中创建Handler
② 重写handleMessage()方法,该方法传入了Message参数.(来自其他线程的message)
③ 子线程通过sendMessage()方法发送信息.该方法需要传入一个Message参数.
通过Handler的obtainMessage()方法获取到一个Message实例. Message的几个属性:
Message.what ---------> 用来标识信息的int值,主线程可据此判断来自不同地方的信息源.
Message.org1/Message.org2 ----------------> 如果只需要存储几个整数值,arg1和arg2是较低成本的替代方法.
Message.obg -------------------> 传递消息的任意对象.
④ 消息发出后,Handler所在的线程通过handleMessage()方法就可以收到具体的消息了,通过msg.what区分信息源.
2.2 线程间通信
Handler不仅仅能将子线程的数据发送给主线程,还适用于任意两个线程之间的通信.创建两个线程进行通信.
private Handler mHandler;
private Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// Looper.prepare();
mHandler = new Handler(){
@Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, (String) msg.obj,Toast.LENGTH_SHORT).show();
}
});
break;
case 2:
break;
}
}
};
// Looper.loop();
}
});
private Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
Message message = mHandler.obtainMessage();
message.what = 1;
message.arg1 = 2;
message.obj = "收到来自非主线程的消息1";
mHandler.sendMessage(message);
}
});
创建了两个Thread,thread接收处理消息,thread1进行发送消息.运行发现报错,说我们创建的handler没有调用Looper.prepare()方法,在handler实例化前加上该方法,发现虽然不报错,但是thread并没有接收到消息,在handler实例化之后加上Looper.loop()方法,运行后可以接收到消息.接着进行Handler原理分析.
为什么在子线程中实例化Handler不调用Looper.prepare()就会报错呢?
当我们在new Handler时.
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
要想在线程里创建handler对象,必须先调用Looper.prepare()方法.
将当前线程初始化为循环程序。这使您有机会在实际开始循环之前创建引用该循环程序的处理程序。请确保在调用此方法后调用{@link #loop()},并通过调用{@link #quit()}结束该方法。
也就是说只有调用该方法后,才有机会创建该线程的handler.
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));
}
在当前线程创建了一个Looper.
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper的构造函数里又为我们创造了一个MessageQueue对象.
主线程中创建Handler不需要调用Looper.prepare()方法和Looper.loop()是因为在App初始化的时候都会执行ActivityThread的main方法.
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
在创建主线程的时候Android已经调用了Looper.prepareMainLooper()方法和Looper.loop()方法,所以我们在主线程中可以直接创建handler使用.
Handler发送消息的过程.
在绝对时间(以毫秒为单位)uptimeMillis之前的所有挂起消息之后将消息排队到消息队列中。时基是{@link android.os.SystemClock#uptimeMillis}。你将收到它在{@link #handleMessage},在线程附加到这个处理程序。
个人理解: 该方法就是将消息添加到消息队列中 在设置的时间发出 在我们的handler线程中我们将会收到该消息
参数: Message对象 uptimeMillis 传递消息的绝对时间
如果成功地将消息放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的looper正在退出。注意,true的结果并不意味着该消息将被处理——如果looper在消息的交付时间之前退出,那么该消息将被删除。
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);
}
sendMessage关键在于enqueueMessage()方法.
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage()方法内部调用了MessageQueue的enqueueMessage()方法.
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
synchronized (this) {
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
没太看懂,有点懵.借助参考博客的理解:
Message被存入到MessageQueue时,是将Message存入到了一个Message.next上.形成了一个链式的列表,同时也保证了Message列表的时序性.
Message的发送实际是放入到了Handler对应线程的MessageQueue中,Message如何被取出的.看下Looper.loop()方法.
/**
* 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();
// 调用该方法前 需先调用Looper.prepare()方法,否则抛出异常.
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();
}
}
该方法的注释: 在这个线程中运行消息队列.意思是handler发送消息会把消息加入到消息队列当中,但需要调用该方法让消息队列运行起来,不然我们收不到消息.
myLooper()方法里取出了当前线程的Looper对象,然后在Looper对象内开启了一个死循环 不断从Looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用dispatchMessage去分发消息,target就是我们创建的handler
分发消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
如果设置了callback,则会直接调用handleCallback()方法,即直接调用run方法.如果没有设置,则直接调用handler本身的handleMessage方法,由于handler是在主线程中创建的,所以handleMessage()方法也会执行在主线程中.
3. 总结
1.使用handler时候,首先要保证调用了Looper.prepare()方法,主线程中已经默认调用过,可以直接创建handler.该方法会将我们当前的线程初始化为一个循环程序,即创建出唯一的Looper对象.每个线程只有一个Looper.Looper对象内部又维护有唯一的一个MessageQueue,所以一个线程可以有多个handler,但只能有一个Looper和一个MessageQueue.,
2.Message在MessageQueue不是通过一个列表来存储的,而是将传入的Message传入到上一个Message的next中,在取出的时候通过顶部的Message就能按放入的顺序依次取出Message.
3.Looper对象通过loop()方法让消息队列在该线程中运行起来,不断从消息队列中取出Message.然后将消息分发给handler.message.target即为创建的handler,这样消息就传到了handler所在的线程.