本篇主要是简单介绍消息机制模型和各组件的了解,源码分析请见Handler,Looper,MessageQueue,android中的线程通信以及源码分析(一)
让我们从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 represent 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());
}
}
<strong>mLooper = Looper.myLooper();</strong>
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;
}
mLooper是handler中一个私有成员Looper,至于Looper是什么欢迎看上一篇博文~我们先接着往下看,在这里调用了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();
}
sThreadLocal是Looper中的一个final的静态成员ThreadLocal,如下图:
<span style="font-size:14px;"> // sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();</span>
下面我简单先说一下什么是ThreadLocal。(有关Threadlocal细节)
从字面上来看,线程本地变量。什么意思?就是针对每一个线程都会有一个变量的线程副本存在。不理解没关系,接着往下看。
在Thread线程中有一个成员变量Threadlocal.Values
/**
* Normal thread local values.
*/
ThreadLocal.Values localValues;
可这又是什么呢?
在Threadlocal.Values当中的私有成员table,用来存放变量副本。
/**
* Map entries. Contains alternating keys (ThreadLocal) and values.
* The length is always a power of 2.
*/
private Object[] table;
说白了,ThreadLocal就是针对每一个线程都维护了一个Values对象,Values中的table是真正存放变量副本的一张表。
这个问题现在理解起来可能比较困难,我们先放一放,接着看下面的内容。
Looper在初始化的时候都会调用它的静态方法prepare,并且在new Looper的同时完成了消息队列的初始化。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
<strong>sThreadLocal.set(new Looper(quitAllowed));</strong>
}
private Looper(boolean quitAllowed) {
<strong>mQueue = new MessageQueue(quitAllowed);</strong>
mThread = Thread.currentThread();
}
在prepare里面可以看见,首先调用threadlocal的get方法,这个方法做了什么事情呢?是把该线程中所保存的线程副本Looper对象取出来,如果该对象已存在,根据一个线程中只能有一个Looper,会报出异常,否则new一个新的Looper对象并放入线程副本中。
那么接下来,我来解释一下什么是线程副本。
之前有说过每一个线程中都维护了一个Looper,可是每一个线程中的Looper又互不干扰,各司其职。在这样的情况下,通过ThreadLocal来对每一个线程维护一个Looper实例,各自不互相干扰。如果是所有的线程都共用一个Looper的话,首先资源开销会很大,而且由于队列可能会很长,消息分发会不及时等等。还有可能会因为并发带来阻塞等问题。而采取在每一个有需要使用Looper的线程中,用ThreadLoca对这一Looper维护,更有效的利用了资源,效率也会更高。这就是线程副本的作用。
正常在使用handler消息机制时应该是例如下面的写法:
new Thread(new Runnable() {
@Override
public void run() {
<strong>Looper.prepare();</strong>
Log.i("XXX", "创建handler");
<strong>handler = new Handler()</strong> {
};
<strong>Looper.loop();</strong>
}
}).start();
首先Looper.prepare()在线程中创建Looper实例和消息队列。然后实例化handler并完成对当前线程looper和队列的绑定。所以我们可以看到,handler执行的线程是looper所处的线程。而默认构造函数的handler默认是和当前所在线程绑定的。当然我们也可以通过如下构造,为handler指定looper,即绑定了looper所在的线程。
public Handler(<span style="color:#ff6666;">Looper looper</span>, Callback callback, boolean async) {
<strong>mLooper = looper</strong>;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
最后是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();
<strong>for (;;)</strong> {
M<strong>essage msg = queue.next();</strong> // 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);
}
<strong>msg.target.dispatchMessage</strong>(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();
}
}
我们可以看红色的代码部分。首先loop()里面存在一个死循环,那么在死循环中做了哪些事情?
Message msg = queue.next(); // might block
取出消息队列中的下一个Message,如果消息队列中没有消息则会一直阻塞,直到有消息位置才会返回消息。如果返回的是一个null值时,说明消息对流已经关闭,对应的Looper也应该关闭,return掉。 msg.target.dispatchMessage(msg);
在这一行代码中,Message对象持有一个handler的引用target,即调用Message对应的handler的dispatch方法将消息分发出去。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
<strong>handleCallback(msg);</strong>
} else {
if (mCallback != null) {
if (<strong>mCallback.handleMessage(msg)</strong>) {
return;
}
}
<strong>handleMessage(msg);</strong>
}
}
在dispatch中设计到一个优先级的问题。
优先执行massage中实现的callback
private static void handleCallback(Message message) {
message.callback.run();
}
其次是handler中实现的Callback接口 /**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
最后才是官方例子中所示,也是我们最常用的handleMessage方法
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
首先相信大家都知道,更新UI的几种方式:
· Activity.runOnUiThread(Runnable)
· View.post(Runnable)
· View.postDelayed(Runnable, long)
· Handler
这几种方式其实都是采用的handler中的post方法,而后调用sendMessageAtTime方法。
/**
* 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 <strong>enqueueMessage(queue, msg, uptimeMillis)</strong>;
}
enqueueMessage方法是将message加入队列的方法,如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
调用了消息队列中的加入队列方法。
队列中的Message又是怎么存取的呢?
Message中有一个指向下一个Message的引用next。那么我们不难理解,Message使用了最简单的链式结构,而并没有使用像是并发处理中的竞争队列之类的结构。Message的存取是一个FIFO(先进先出。。虽然我这句是废话。。。。大家一定都知道)队列。
// sometimes we store linked lists of these things
/*package*/ Message next;
Message实现了Parceble,用于消息传递
public final class Message implements Parcelable
public static final Parcelable.Creator<Message> CREATOR
= new Parcelable.Creator<Message>() {
public Message createFromParcel(Parcel source) {
Message msg = Message.obtain();
msg.readFromParcel(source);
return msg;
}
public Message[] newArray(int size) {
return new Message[size];
}
};
android线程模型是一个单线程模型,即只有UI线程负责更新UI。因为虽然cpu可能会有多个,但是显示只有一个。
为了遵从单线程模型,费时操作必须在子线程中实现。这样的话就涉及到子线程和主线程之间的通信,由此引入了消息机制。
还有就是可能会有人说以前没有用过prepare和loop也可以直接使用handler。那是因为在主线程中,默认有主线程looper,在主线程启动时就已经prepare并且loop了,自然就不用我们自己去实现~
下面这段代码就是ActivityThread中的主函数:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("<pre-initialized>");
<strong>Looper.prepareMainLooper();</strong>
// 创建ActivityThread实例
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
<strong>Looper.loop();</strong>
throw new RuntimeException("Main thread loop unexpectedly exited");
}
----------割割割-------------
自问自答好了。。
http://stackoverflow.com/questions/6984263/android-looper-and-call-stack
everything that happens on the UI thread is through that loop
这样看来。。确实android的显示就是一个loop的死循环