Handler,Messgae,MessageQueue,Looper分析
2016-03-21 01:48
363 查看
首先,明确一点,一个线程只能创建一个Looper,只有一个MessageQueue,但是可以有多个Handler来发消息
如上可知,在for死循环之中反复的去消息,queue.next()取出下一条消息,然后调用Message中的target(Handler)的dispatchMessage()方法:
由代码可知Looper取出消息后会执行到这里,而Handler发送的Message如果有设置callback(Runnable)的话就会直接执行run方法,如果Handler有设置Callback那就直接调用Callback的handleMessage方法直接把消息处理了,只有以上两者条件都不满足才会最后去调用Handler的handleMessage()方法,而Handler的handleMessage()方法就是我们自己去重载的方法,到这消息处理结束,然后进入下一个循环;
这里面需要注意一个问题:在线程中初始化Looper的步骤
必须这么调用,因为loop是死循环,所以在loop方法后面的代码都不能执行到,并且prepare()方法再一个线程中不能重复调用两次,会报错的“Only one Looper may be created per thread”
就是这段代码控制一个线程只能有一个Looper的
所有的sendMessage方法或者post方法最终都是调用以上方法把Message压入线程中对应的MessageQueue的尾部,另外声明一点,Handler中的一些延迟发送Message例如sendMessageDelay等方法在cup休眠的时候是会跟着休眠,暂停执行,要等cup唤醒后才重新开始计时延迟,然后再执行
clearForRecycle()是把被处理过的Message中的状态全部都设置成初始状态,然后把当前Message对象赋值给sPool,表示当前这个Message是可用的空消息,在obtain()里面就会去判断是否存在可用消息,如果存在那么久直接取出来而不用再去new Message()占内存
只不过HandlerThread多了一个可以执行耗时操作功能而已
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再被回收重置状态供下一次使用相关文章推荐
- //var_dump(get_defined_constants(True));获取系统常量
- Uiautomator 2.0之UiDevice新增API学习小记
- Vue.js
- StringBuilder & StringBuffer
- 关于UIView的Alpha和NSDate的两个使用方法
- 【安卓】UI笔记
- QuiLoader 动态加载 QMainWindow
- Poj 2299 - Ultra-QuickSort 离散化,树状数组,逆序对
- QT 动态加载UI文件注意事项
- JSP局部刷新,子页面中的EasyUI失效问题解决
- IOS基础控件-UITableView
- IOS开发之UIView总结
- Servlet - Request、Session、servletContext区别
- java.sql.SQLException: No suitable driver found for jdbc
- apue- chapter 1 UNIX基础知识
- UITableView中的cell重用(原理)以及cell重用解决办法
- UI 进阶总结
- UE/UI/UCD/UED 区别
- 单线程模型中Message,Handler,MessageQueue,Looper之间的关系
- CoordinatorLayout使用方法