AndroID消息循环机制源码
前言:
搞AndroID的不懂Handler消息循环机制,都不好意思说自己是AndroID工程师。面试的时候一般也都会问这个知识点,但是我相信大多数码农肯定是没有看过相关源码的,顶多也就是网上搜搜,看看别人的文章介绍。学姐不想把那个万能的关系图拿出来讨论。
近来找了一些关于androID线程间通信的资料,整理学习了一下,并制作了一个简单的例子。
andriod提供了 Handler 和 Looper 来满足线程间的通信。例如一个子线程从网络上下载了一副图片,当它下载完成后会发送消息给主线程,这个消息是通过绑定在主线程的Handler来传递的。
在AndroID,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事androID的新 概念。我们的主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,我们引入一个新的机制Handle,我们有消息循环,就要往消息循环里 面发送相应的消息,自定义消息一般都会有自己对应的处理,消息的发送和清除,消息的的处理,把这些都封装在Handle里面,注意Handle只是针对那 些有Looper的线程,不管是UI线程还是子线程,只要你有Looper,我就可以往你的消息队列里面添加东西,并做相应的处理。
但是这里还有一点,就是只要是关于UI相关的东西,就不能放在子线程中,因为子线程是不能 *** 作UI的,只能进行数据、系统等其他非UI的 *** 作。
在AndroID,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个是androID的新概念。我们的主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,我们引入一个新的机制Handler,我们有消息循环,就要往消息循环里面发送相应的消息,自定义消息一般都会有自己对应的处理,消息的发送和清除,把这些都封装在Handler里面,注意Handler只是针对那 些有Looper的线程,不管是UI线程还是子线程,只要你有Looper,我就可以往你的消息队列里面添加东西,并做相应的处理。
但是这里还有一点,就是只要是关于UI相关的东西,就不能放在子线程中,因为子线程是不能 *** 作UI的,只能进行数据、系统等其他非UI的 *** 作。
先从我们平时的使用方法引出这个机制,再结合源码进行分析。
我们平时使用是这样的:
//1. 主线程 Handler handler = new MyHandler(); //2. 非主线程 HandlerThread handlerThread = new HandlerThread("handlerThread"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()); //发送消息 handler.sendMessage(msg); //接收消息 static class MyHandler extends Handler { //对于非主线程处理消息需要传Looper,主线程有默认的sMainLooper public MyHandler(Looper looper) { super(looper); } @OverrIDe public voID handleMessage(Message msg) { super.handleMessage(msg); } }
那么为什么初始化的时候,我们执行了1或2,后面只需要sendMessage就可处理任务了呢?学姐这里以非主线程为例进行介绍,handlerThread.start()的时候,实际上创建了一个用于消息循环的Looper和消息队列MessageQueue,同时启动了消息循环,并将这个循环传给Handler,这个循环会从MessageQueue中依次取任务出来执行。用户若要执行某项任务,只需要调用handler.sendMessage即可,这里做的事情是将消息添加到MesSAEQueue中。对于主线程也类似,只是主线程sMainThread和sMainLooper不需要我们主动去创建,程序启动的时候Application就创建好了,我们只需要创建Handler即可。
我们这里提到了几个概念:
HandlerThread 支持消息循环的线程 Handler 消息处理器 Looper 消息循环对象 MessageQueue 消息队列 Message 消息体对应关系是:一对多,即(一个)HandlerThread、Looper、MessageQueue -> (多个)Handler、Message
源码解析
1. Looper
(1)创建消息循环
prepare()用于创建Looper消息循环对象。Looper对象通过一个成员变量ThreadLocal进行保存。
(2)获取消息循环对象
myLooper()用于获取当前消息循环对象。Looper对象从成员变量ThreadLocal中获取。
(3)开始消息循环
loop()开始消息循环。循环过程如下:
每次从消息队列MessageQueue中取出一个Message
使用Message对应的Handler处理Message
已处理的Message加到本地消息池,循环复用
循环以上步骤,若没有消息表明消息队列停止,退出循环
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));}public static Looper myLooper() { return sThreadLocal.get();}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(); }}
2. Handler
(1)发送消息
Handler支持2种消息类型,即Runnable和Message。因此发送消息提供了post(Runnable r)和sendMessage(Message msg)两个方法。从下面源码可以看出Runnable赋值给了Message的callback,最终也是封装成Message对象对象。学姐个人认为外部调用不统一使用Message,应该是兼容Java的线程任务,学姐认为这种思想也可以借鉴到平常开发过程中。发送的消息都会入队到Messagequeue队列中。
(2)处理消息
Looper循环过程的时候,是通过dispatchMessage(Message msg)对消息进行处理。处理过程:先看是否是Runnable对象,如果是则调用handleCallback(msg)进行处理,最终调到Runnable.run()方法执行线程;如果不是Runnable对象,再看外部是否传入了Callback处理机制,若有则使用外部Callback进行处理;若既不是Runnable对象也没有外部Callback,则调用handleMessage(msg),这个也是我们开发过程中最常覆写的方法了。
(3)移除消息
removeCallbacksAndMessages(),移除消息其实也是从MessageQueue中将Message对象移除掉。
public voID handleMessage(Message msg) {}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();}public final Message obtainMessage(){ return Message.obtain(this);}public final boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r),0);}public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg,0);}private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m;}public final boolean sendMessageDelayed(Message msg,long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg,SystemClock.uptimeMillis() + delayMillis);}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);}private boolean enqueueMessage(MessageQueue queue,Message msg,long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg,uptimeMillis);}public final voID removeCallbacksAndMessages(Object token) { mQueue.removeCallbacksAndMessages(this,token);}
3. MessageQueue
(1)消息入队
消息入队方法enqueueMessage(Message msg,long when)。其处理过程如下:
待入队的Message标记为InUse,when赋值
若消息链表mMessages为空为空,或待入队Message执行时间小于mMessage链表头,则待入队Message添加到链表头
若不符合以上条件,则轮询链表,根据when从低到高的顺序,插入链表合适位置
(2)消息轮询
next()依次从MessageQueue中取出Message
(3)移除消息
removeMessages()可以移除消息,做的事情实际上就是将消息从链表移除,同时将移除的消息添加到消息池,提供循环复用。
boolean enqueueMessage(Message msg,long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue",e); msg.recycle(); return false; } msg.markInUse(); 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 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; }}voID removeMessages(Handler h,int what,Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } }}
4. Message
(1)消息创建
Message.obtain()创建消息。若消息池链表sPool不为空,则从sPool中获取第一个,flags标记为UnInUse,同时从sPool中移除,sPoolSize减1;若消息池链表sPool为空,则new Message()
(2)消息释放
recycle()将消息释放,从内部实现recycleUnchecked()可知,将flags标记为InUse,其他各种状态清零,同时将Message添加到sPool,且sPoolSize加1
/** * Return a new Message instance from the global pool. Allows us to * avoID allocating new objects in many cases. */public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message();}/** * Return a Message instance to the global pool. * <p> * You MUST NOT touch the Message after calling this function because it has * effectively been freed. It is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a Handler. * </p> */public voID recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked();}/** * Recycles a Message that may be in-use. * Used internally by the MessageQueue and Looper when disposing of queued Messages. */voID recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUID = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POol_SIZE) { next = sPool; sPool = this; sPoolSize++; } }}
5. HandlerThread
由于Java中的Thread是没有消息循环机制的,run()方法执行完,线程则结束。HandlerThread通过使用Looper实现了消息循环,只要不主动调用HandlerThread或Looper的quit()方法,循环就是一直走下去。
public class HandlerThread extends Thread {int mPriority;int mTID = -1;Looper mLooper;public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAulT;}@OverrIDepublic voID run() { mTID = Process.myTID(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTID = -1;}public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started,wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper;}public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false;}}
总结
关键类:HandlerThread、Handler、Looper、MessageQueue、Messaga MessageQueue数据结构,链表。感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
以上是内存溢出为你收集整理的Android消息循环机制源码深入理解全部内容,希望文章能够帮你解决Android消息循环机制源码深入理解所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)