在线程内部有一个或者多个Hadnler对象,外部程序通过该Handler对象向线程发送异步消息,消息经由Hadnler传递到MessageQueue对象中,线程内部只能包含一个MessageQueue对象,主线程执行函数中从MessageQueue中读取消息,并回调Handler对象中的函数handleMessage()。
为更好地理解Handler的工作原理,先介绍有Handler一起工作的几个逐渐:
Message:Handler接收和处理的消息对象。
Looper:每个线程只能拥有一个Looper,它的loop方法负责读取MessageQueue中的消息,读到消息之后就把消息交给该消息对应的Hadnler进行处理。
MessageQueue:消息队列,它采用先进的方式来管理Message,程序创建Looper对象时会在它的构造器中创建Looper对象。
下面是线程内部Handler、MessageQueue、Looper类的调用过程。
我们通过调用Looper类的静态方法prepare()为线程创建MessageQueue对象,prepare()函数的代码如下:
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)); }
在该段代码中,变量sThreadLocal的类型是ThreadLocal,该类的作用是提供“线程局部存储”(在本线程内的任何对象保持一致),sThreadLocal对象会根据调用该prepare()函数的线程的id保存一个数据对象,这个数据对象就是所谓的“线程局部存储”对象,该对象是通过sThreadLocal的set()方法设置进去的,Looper类中保存的这个对象是一个Looper对象。
prepare()函数的第一行先用get()方法去获取该线程对应的Looper对象,如果已经有的话,那么出错(因为一个线程只能有一个Looper对象,因为一个异步线程只能有一个消息队列),如果没有,创建一个新的Looper对象。
Looper的作用有两个:为该类静态函数的额prepare()的线程创建一个消息队列,第二个是提供静态的loop()函数,使调用该函数的线程进行无限的循环,并从消息队列中读取消息。
下面是Looper()对象的源码:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed);//创建一个消息队列 mRun = true; mThread = Thread.currentThread(); } public static void loop() { final Looper me = myLooper();//返回当前的Looper对象,通过sThreadLocal的get方法 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(); <span style="white-space:pre"> </span>//进入无限循环 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);//完成对该消息的处理,也就是说,消息的具体处理 //实际上是由程序指定的,msg变量的类型是Message,
//msg.target的类型是Handler 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(); //每当处理完消息都需要调用,回收该Message对象占用
//的系统资源,因为Message类内部使用了一个数据池保存Message //对象,从而避免了不停地创建和删除Message类对象,因此每次///处理完消息都需要将该Message对象表明为空,以便该对象可以被重用 } }
下面说说MessageQueue。
该消息队列采用排队的方式对消息进行处理,即先到先处理,但如果消息本身被指定了被处理的时间,那么必须得等到该时间。队列中的消息以链表的结构进行保存。Message内部有一个next变量,指向下一个消息。
MessageQueue中主要有两个函数“取出消息”和“添加消息”。
分别为函数next()和enquenceMessage().
next()函数
先调用nativePollOnce(mPtr,int time)是一个JNI函数,他的作用是从消息队列中取出一个消息。MessageQueue本身并没保存消息队列,真正的消息队列数据保存在JNI中的C
代码中。也就是说会在C中创建一个NativeMessageQueue,这就是nativePollOnce第一个参数为int型变量的意义,在C中,该变量被强制转化为一个NativeMessageQueue对象,在C环境中,如果消息队列中没有消息,将导致当前线程被挂起,如果有,则C代码中将把该消息赋值给Java环境中的mMessages变量。
接下来的这段代码被包含在synchronize(this)关键字中,this被用作取消息和写消息的锁,这部分仅仅判断所指定的执行时间是否到了,如果到了,就返回该消息,并将mMessage变量置空。如果还没到,则尝试读取下一个信息。
如果mMessage为空,说明C环境中的消息队列没有可以执行的消息了,因此,执行mPendingIdleHandlers列表中的“空闲回调函数”,我们可以在MessageQueue中注册一些“空闲回调函数”,从而当线程中没有消息可以去执行这些“空闲代码”
final Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { if (mQuiting) { return null; } // 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); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // 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; } }
下面是enquenceMessage(),
该函数分两步:
将参数msg赋值给mMessages.
调用nativeWake(mPtr),这也是一个JNI函数,其内部会将mMessage消息添加到C环境中的消息队列中,并且如果消息线程处在挂起状态,则唤醒该线程。
final boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } if (msg.target == null) { throw new AndroidRuntimeException("Message must have a target."); } boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; } msg.when = when; Message p = mMessages; 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; } } if (needWake) { nativeWake(mPtr); } return true; }
下面是Handler的构造函数。
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) { //次数说明必须在构造Handler对象之前执行Looper.prepare()操作 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
JNI简介:Java Native Interface,是Java的本地接口,所谓本地一般是指C/C++.当使用JAVA进行程序设计的时候,一般有一下几种情况需要C语言的协助
调用驱动,操作系统提供的驱动一般都是C接口,Java本身不具备操作这些驱动的能力
对于某些大量数据的处理磨矿,Java的执行效率可能远低于C,因此希望用C去完成。
某些功能模块,可能两者执行效率差不多,但是已经存在C代码了,不想用Java再次去写,只想利用已有的C代码。