博文来源:http://blog.csdn.net/shift_wwx
对于普通的线程,运行完run() 就会结束。但是异步消息处理线程,是指线程启动后会进入一个无限的循环中,每循环一次,就会从内部的消息队列中取出一个消息,并会调用相应的消息处理函数,执行完一个消息会继续循环。如果消息队列为空,线程会暂停,直到消息队列里面有新的消息。
1. Andorid实现异步线程方法:
在线程内部可以有一个或者多个Handler对象,外部通过Handler向异步线程发送消息,消息由Handler传给MessageQueue对象中。线程内部只能包含一个MessageQueue对象,线程主执行函数中从MessageQueue中读取消息,并回调Handler对象中的回调函数handleMessage()。
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
Looper的作用有两点,第一是调用静态函数prepare()为线程创建一个消息队列;第二是调用静态函数loop(),使调用该函数的线程进行无限循环,并从消息队列中读取消息。
/** 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)); }code中需要了解的有两点:
a). sThreadLocal对象
b). new Looper(quitAllowed)
在代码中,变量sThreadLocal的类型是ThreadLocal,该类的作用是提供“线程局部存储”,那什么是线程局部存储(TLS)呢?这个问题可以从变量的作用域的角度来理解。
变量常见作用域分为以下几种:
(1)函数内部的变量:其作用域是该函数,在函数调用的时候,该变量会重新初始化。
(2)类内部的变量:其作用域是该类对象,即只要对象没有销毁,那么这个变量在对象内部的值是不变的。
(3)类内部的局部变量:其作用域是整个进程,则只要在该进程中个,这个变量的值是一直保持,无论该类构造过多少对象,这个值是一样的。
对于类内部的变量而言,无论从进程中的哪个线程引用该变量,其值总是一样的。因为在编译器内部为这个局部变量独立分配了一个空间。但是,有时候我们想从一个线程中引用这个变量的值是一样的,不同的线程引用这个变量的时候值是不一样的,即我们需要一种作用域为线程变量定义,这就是”线程局部存储“。
public class ThreadLocal<T> { /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */ /** * Creates a new thread-local variable. */ public ThreadLocal() {} /** * Returns the value of this variable for the current thread. If an entry * doesn't yet exist for this variable on this thread, this method will * create an entry, populating the value with the result of * {@link #initialValue()}. * * @return the current value of the variable for the calling thread. */ @SuppressWarnings("unchecked") public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); } /** * Provides the initial value of this variable for the current thread. * The default implementation returns {@code null}. * * @return the initial value of the variable. */ protected T initialValue() { return null; } /** * Sets the value of this variable for the current thread. If set to * {@code null}, the value will be set to null and the underlying entry will * still be present. * * @param value the new value of the variable for the caller thread. */ public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }上面是ThreadLocal.java中的部分code,可以看出ThreadLocal是个模板,这样就方便各种不同的对象类型。例如,Looper中的sThreadLocal是这样定义的:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();详细的code可以参考/libcore/luni/src/main/java/java/lang/ThreadLocal.java
Looper.prepare()中另一点就是Looper的构造函数:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }从之前prepare中可以看出,sThreadLocal.get()!=null的时候,会出现exception提示不能重复prepare,也就是说一个线程只能调用一次prepare,只能创建一个Looper对象,也只能创建唯一的MessageQueue对象。
注意:
(1)Activity的MainUI线程默认是有消息队列的。所以在Activity中新建Handler时,不需要先调用Looper.prepare()。
主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。
其他非主线程,不会自动创建Looper,要需要的时候,通过调用prepare函数来实现。
这里可以简单的说一下,在应用程序启动的时候会创建一个thread,ActivityThread.main() :
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()); Security.addProvider(new AndroidKeyStoreProvider()); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); 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")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }详细的不做说明,针对应用程序进程启动过程,网上资料很多,后期也可能做一些自己的分析和补充。
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.recycle(); } }code中可以看出如下几点:
(1)loop函数必须是在prepare函数之后调用,不然会有提示:
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");(2)for循环是无限循环,会一直取消息,如果没有消息,会暂时的挂起来
/** * 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); } }new Handler的时候如果定义了Runnable的话就会调用handleCallback(msg),如果定义了final Callback mCallback;就会调用mCallback.handleMessage(msg),如果重定义了handleMessage()就会调用handleMessage(),所以,一般我们在定义Handler的时候都会重定义handleMessage(),为了回调。
/** * Return a Message instance to the global pool. You MUST NOT touch * the Message after calling this function -- it has effectively been * freed. */ public void recycle() { clearForRecycle(); synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
3. MessageQueue
消息队列采用的是排队方式对消息进行处理,即先到的消息会先处理,但如果消息本身被指定了处理时间,则必须等到该时刻才能处理该消息。
MessageQueue与native的联系比较多,从MessageQueue创建就开始了:
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); return reinterpret_cast<jint>(nativeMessageQueue); }在nativeInit中,new了一个Native层的MessageQueue的对象,并将其地址保存在了Java层MessageQueue的成员mPtr中,Android中有好多这样的实现,一个类在Java层与Native层都有实现,通过JNI的GetFieldID与SetIntField把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,比如Parcel。
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); } }在NativeMessageQueue的构造函数中获得了一个Native层的Looper对象,Native层的Looper也使用了线程本地存储,注意new Looper时传入了参数false。
有一篇博文很不错,可以借鉴。http://www.cnblogs.com/angeldevil/p/3340644.html
4. Handler
尽管MessageQueue提供了直接读/写的函数接口,但对于应用程序员而言,一般不直接读/写消息队列。
程序员一般使用Handler类向消息队列发送消息,并重载Handler的handleMessage函数添加消息处理代码。
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); }通过MessageQueue 中可以看到,消息队列处理是需要这个target, 也就是Handler。
handler对象只能添加到有消息队列的线程中,否则会发生异常。因此,在构造Handler对象前,必须已经执行过Looper.prepare(),但prepare()不能被执行两次。
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; }
一个线程中可以包含多个Handler对象。在Looper.loop函数中,不同的Message对应不同的Handler对象,从而回调不同的handleMessage函数。
具体事例不做介绍,可以尝试线程与主线程、主线程与其他线程之间通信。至于发msg可以看一下:Handler sendMessage 与 obtainMessage (sendToTarget)比较
小结:
1)Handler的处理过程运行在创建Handler的线程里
2)一个Looper对应一个MessageQueue
3)一个线程对应一个Looper
4)一个Looper可以对应多个Handler
5)不确定当前线程时,更新UI时尽量调用post方法
异步消息处理线程处理用于多线程的消息传递外,它还和跨进程调用(IPC)一起被使用,用于实现异步跨进程调用。