您的位置:首页 > 产品设计 > UI/UE

Handler,Messgae,MessageQueue,Looper分析

2016-03-21 01:48 363 查看
首先,明确一点,一个线程只能创建一个Looper,只有一个MessageQueue,但是可以有多个Handler来发消息

Looper

Looper里面包装了一个消息队列MessageQueue,Looper.prepare(),方法是用来初始化该线程的Looper信息,例如new一个Looper出来,然后保存到ThreadLocal,这样可以确保这个线程只有一个Looper,因为ThreadLocal可以实现线程隔离;其中的核心方法就是loop(),此方法就是循环的从MessageQueue中取出消息出来然后处理:

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();
}
}


如上可知,在for死循环之中反复的去消息,queue.next()取出下一条消息,然后调用Message中的target(Handler)的dispatchMessage()方法:

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}


由代码可知Looper取出消息后会执行到这里,而Handler发送的Message如果有设置callback(Runnable)的话就会直接执行run方法,如果Handler有设置Callback那就直接调用Callback的handleMessage方法直接把消息处理了,只有以上两者条件都不满足才会最后去调用Handler的handleMessage()方法,而Handler的handleMessage()方法就是我们自己去重载的方法,到这消息处理结束,然后进入下一个循环;

这里面需要注意一个问题:在线程中初始化Looper的步骤

class LooperThread extends Thread {
public Handler mHandler;

public void run() {
Looper.prepare();

//do something

Looper.loop();
}
}


必须这么调用,因为loop是死循环,所以在loop方法后面的代码都不能执行到,并且prepare()方法再一个线程中不能重复调用两次,会报错的“Only one Looper may be created per thread”

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的

Handler

handler负责往消息队列里面发送消息

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}


所有的sendMessage方法或者post方法最终都是调用以上方法把Message压入线程中对应的MessageQueue的尾部,另外声明一点,Handler中的一些延迟发送Message例如sendMessageDelay等方法在cup休眠的时候是会跟着休眠,暂停执行,要等cup唤醒后才重新开始计时延迟,然后再执行

Message

在创建消息的时候最好调用Handler.obtain()或者Message.Obtain()方法来获取一个可用的消息,尽量不要选择去new一个新的Message;因为在Looper处理完消息后会调用Message.recycle()方法:

public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}

public void recycle() {
clearForRecycle();

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}

void clearForRecycle() {
flags = 0;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}


clearForRecycle()是把被处理过的Message中的状态全部都设置成初始状态,然后把当前Message对象赋值给sPool,表示当前这个Message是可用的空消息,在obtain()里面就会去判断是否存在可用消息,如果存在那么久直接取出来而不用再去new Message()占内存

Handler和HandlerThread

在主线程new出来的Handler默认都已经有mainLooper了,所以可以直接发消息执行,而且Handler的handleMessage()等消息处理方法都是运行在主线程中的,所以不能执行耗时操作,执行耗时操作会有可能造成界面卡顿报错,所以我们一般用来刷新UI;而android提供了HandlerThread来让我们可以执行耗时操作,HandlerThread是一个单独的线程,所以它有自己单独的Looper,但是必须先start线程,初始化这个Looper,然后new Handler(HandlerThread.getLooper())把这个线程的Looper转入指定的Handler,这样handler发的消息就都是发到这个Looper的MessageQueue里面来处理了,而Looper是在HandlerThread这个单独线程里面的所以在loop()里面处理Message的时候并不会阻塞主线程,但是能在这个handler里面执行UI刷新操作吗?可以的。HandlerThread实现其实和以下这种方式是一样的:

Looper.prepare();

//do something

Looper.loop();


只不过HandlerThread多了一个可以执行耗时操作功能而已

总结

一个线程只能有一个Looper,一个Looper维护一个MessageQueue,handler就负责往MessageQueue丢消息,Looper就负责循环的从MessageQueue中取消息出来处理,每个Message中的target默认都是发送这个Message的Handler,所以消息被取出来之后就直接交由Handler去调用dispatchMessage()分发处理消息,然后Message再被回收重置状态供下一次使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: