Handler,Messgae,MessageQueue,Looper分析
来源:互联网 发布:淘宝店等级划分 编辑:程序博客网 时间:2024/05/19 14:52
首先,明确一点,一个线程只能创建一个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再被回收重置状态供下一次使用
- Handler,Messgae,MessageQueue,Looper分析
- Handler、Looper、MessageQueue源码分析
- Handler,Looper,MessageQueue原理分析
- Handler源码分析,Handler,Looper,Message,MessageQueue
- Looper,Handler, HandlerThread,Message,MessageQueue分析
- Android Handler Looper MessageQueue原理分析
- Android消息机制:Looper、Handler、MessageQueue分析
- (Handler+Message+Looper+MessageQueue)源码分析
- Handler,MessageQueue,Message,Looper源码分析
- Looper、MessageQueue、Handler机制简要分析
- Handler、Looper、Message、MessageQueue基础流程分析
- Handler、Looper、Message、MessageQueue基础流程分析
- android handler Looper MessageQueue源码分析
- Handler Looper MessageQueue之MessageQueue
- Looper,Handler,Message,MessageQueue
- Message,MessageQueue,Looper,Handler
- Handler,Looper,MessageQueue机制
- Handler,Looper,MessageQueue关系
- RandomizeQueue
- Linux+Nginx+Tomcat+Redis实现负载均衡,应用集群及session共享
- 准程序员的进阶之路
- 学习HTML之前必须了解的基础
- java中File基础
- Handler,Messgae,MessageQueue,Looper分析
- 集合
- Java学习笔记之多态
- Linux服务器架设篇之(一)--源码安装mysql
- 求整数段和
- Mac电脑终端下查看电脑信息的相关指令
- githup 上传文件
- 使用lshw工具查看linux的系统硬件信息
- git github