android之handler messagequene looper threadlocal 之间的详细解读
2016-03-17 15:12
435 查看
说到android的handler,他只是消息机制的上层接口,为什么这么说呢,消息机制真正的实现是在messagequene和looper中进行的
首先我们应该遇到过在一些子线程中new handler会报如下错误
这是为什么呢,因为handler的创建,我们看下handler的初始化过程
这里插个题外话
当你在handler初始化的时候最好用static标示,否则会提示这个警告,有可能会造成oom,
下面我们来分析下源码
首先调用Looper.mylooper().得到一个looper对象,我们点进去看mylooper()的实现如下
是通过ThreadLocal的get方法得到looper对象,对于ThreadLocal的详细解读可以在这我的上篇博客ThreadLocal详细解读,而Threadlocal的set()是在Looper.prepare()中初始化的
这样就是为什么我们在用之前要调Looper.prepare(),如果没调用这个方法,则looper为空,再回到handler中发现如果looper则会抛运行时异常
但是这个方法只能调用一次,否则会抛出
至于主线程,在ActivityThread中已经调用了looper.prepare(),
到此looper的初始化结束,我们来看handler中另外的初始化
这里通过上面初始化的looper的一个成员变量mQueue得到MessageQueue对象,点击进入looper查看mQueue初始化的地方
发现还是在looper.prepare()
我们发现这个构造函数被私有化了,在这里进行初始化MessageQueue,顾名思义,MessageQueue是一个消息通道
当handler发送消息时,有以下几种发送消息方式
最终会调用 MessageQueue的 enqueueMessage(Message msg, long when)来进行消息插入,这里的
msg.target就是一个handler,在这里发送消息时,必须先初始化handler,
既然消息进来了,应该会有个类来负责不断的取消息并处理,我们想到了应该是Looper来处理,看下looper的loop(),方法
在这里,首先通过mylooper()得到当前线程的Looper对象me,再通过me.mQueue变量得到当前looper的MessageQueue对象queue,然后for(;;)循环阻塞,不停的调用queue的next方法,我们点击去看下next方法的实现
其实这里就是从MessageQueue中不断取出message并从MessageQueue中删除取出过的message,再回到我们的loop()方法,我们发现调用完next()方法取出message以后会调用handler的
msg.target.dispatchMessage(msg);
最终会调用handleMessage(msg),我们点进去看下handleMessage()的实现
发现这里什么也没有实现,从官方注释可以看出这里是处理消息回来以后的实现,
自此,我们可以整理出handler是android消息机制的顶层接口,是用之前,先调用looper.prepare()初始化Messagequeue,并将当前线程当前looper保存在ThreadLocal中,然后通过handler发送消息,最终调用MessageQueue的equeueMessage将消息插入到MessageQueue中,然后再调用Looper.loop(),Looper.loop()会调用MessageQueue的next ()方法不断的从MessageQueue中获取消息并删除掉,再通过handler的dispatchMessage()分发消息到handler所在线程中,handler再通过handleMessage进行消息处理
首先我们应该遇到过在一些子线程中new handler会报如下错误
Can't create handler inside thread that has not called Looper.prepare().
这是为什么呢,因为handler的创建,我们看下handler的初始化过程
/** * Use the {@link Looper} for the current thread with the specified callback interface * and set whether the handler should be asynchronous. * * Handlers are synchronous by default unless this constructor is used to make * one that is strictly asynchronous. * * Asynchronous messages represent interrupts or events that do not require global ordering * with respect to synchronous messages. Asynchronous messages are not subject to * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. * * @param callback The callback interface in which to handle messages, or null. * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. * * @hide */ 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; }
这里插个题外话
Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());
当你在handler初始化的时候最好用static标示,否则会提示这个警告,有可能会造成oom,
下面我们来分析下源码
首先调用Looper.mylooper().得到一个looper对象,我们点进去看mylooper()的实现如下
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }
是通过ThreadLocal的get方法得到looper对象,对于ThreadLocal的详细解读可以在这我的上篇博客ThreadLocal详细解读,而Threadlocal的set()是在Looper.prepare()中初始化的
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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.prepare(),如果没调用这个方法,则looper为空,再回到handler中发现如果looper则会抛运行时异常
if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); }
但是这个方法只能调用一次,否则会抛出
if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); }
至于主线程,在ActivityThread中已经调用了looper.prepare(),
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } /** Returns the application's main looper, which lives in the main thread of the application. */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
到此looper的初始化结束,我们来看handler中另外的初始化
mQueue = mLooper.mQueue;
这里通过上面初始化的looper的一个成员变量mQueue得到MessageQueue对象,点击进入looper查看mQueue初始化的地方
发现还是在looper.prepare()
sThreadLocal.set(new Looper(quitAllowed));
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
我们发现这个构造函数被私有化了,在这里进行初始化MessageQueue,顾名思义,MessageQueue是一个消息通道
当handler发送消息时,有以下几种发送消息方式
/** * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in {@link #handleMessage}, * in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } /** * Sends a Message containing only the what value. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } /** * Sends a Message containing only the what value, to be delivered * after the specified amount of time elapses. * @see #sendMessageDelayed(android.os.Message, long) * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } /** * Sends a Message containing only the what value, to be delivered * at a specific time. * @see #sendMessageAtTime(android.os.Message, long) * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageAtTime(msg, uptimeMillis); } /** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } /** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ 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); } /** * Enqueue a message at the front of the message queue, to be processed on * the next iteration of the message loop. You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * <b>This method is only for use in very special circumstances -- it * can easily starve the message queue, cause ordering problems, or have * other unexpected side-effects.</b> * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendMessageAtFrontOfQueue(Message msg) { 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, 0); }
最终会调用 MessageQueue的 enqueueMessage(Message msg, long when)来进行消息插入,这里的
msg.target就是一个handler,在这里发送消息时,必须先初始化handler,
if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); }
既然消息进来了,应该会有个类来负责不断的取消息并处理,我们想到了应该是Looper来处理,看下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(); 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.recycleUnchecked(); } }
在这里,首先通过mylooper()得到当前线程的Looper对象me,再通过me.mQueue变量得到当前looper的MessageQueue对象queue,然后for(;;)循环阻塞,不停的调用queue的next方法,我们点击去看下next方法的实现
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
其实这里就是从MessageQueue中不断取出message并从MessageQueue中删除取出过的message,再回到我们的loop()方法,我们发现调用完next()方法取出message以后会调用handler的
msg.target.dispatchMessage(msg);
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
最终会调用handleMessage(msg),我们点进去看下handleMessage()的实现
/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }
发现这里什么也没有实现,从官方注释可以看出这里是处理消息回来以后的实现,
自此,我们可以整理出handler是android消息机制的顶层接口,是用之前,先调用looper.prepare()初始化Messagequeue,并将当前线程当前looper保存在ThreadLocal中,然后通过handler发送消息,最终调用MessageQueue的equeueMessage将消息插入到MessageQueue中,然后再调用Looper.loop(),Looper.loop()会调用MessageQueue的next ()方法不断的从MessageQueue中获取消息并删除掉,再通过handler的dispatchMessage()分发消息到handler所在线程中,handler再通过handleMessage进行消息处理
相关文章推荐
- uipickerview用法
- @RequestBody, @ResponseBody 注解详解(转)
- AS-->如何更高效的使用 Gradle, 快速build apk
- phpQuery对数据信息的采集进一步学习
- @RequestParam @RequestBody @PathVariable 等参数绑定注解详解
- Android Volley 之自定义Request
- 【笔记】《C#大学教程》- 第12章 GUI(一)
- document.getElementById("XXX").innerHTML与document.getElementById("XXX").value
- FineUI(专业版)v3.0.0 发布,手机、平板和桌面全支持!
- 关于UIButton setImage 不显示 和 setTitle不能和谐相处的问题
- 多字典同key时对value加法操作
- iOS 子视图 父视图 UIView 相关的方法
- JFinal教程JfinalUIB 代码笔记 (8)--- 权限设计和分配
- UIBezierPath的使用
- hdu 4632 Palindrome subsequence 区间dp
- JFinal教程JfinalUIB 代码笔记 (7)--- URL拦截规则和修改
- 高逼格UI-ASD(Android Support Design)
- Github GUI 托管代码教程
- codeforces_622A. Infinite Sequence
- ios蓝牙开发 ------ CoreBluetooth 教程<转>