要了解Handler的话,首先要了解一下Looper与MessageQueue机制
- looper 是android里面的消息泵,不断的从MessageQueue内抽取消息
- MessageQueue 存储消息的队列是一个链表
MessageQueue没什么好讲的主要是Looper的机制,需要介绍下,Looper里面有一个比较重要的属性ThreadLocal
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);
}
Values就是与该线程挂钩的变量集合,然后我们来看看Looper里面的函数,启动一个Looper的时候我们都需要调用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有点static的性质只会被初始化一次 也就保证了一个线程只会拥有一个Looper的一对一关系,每个Looper开始前都会调用这个函数,然后看看最重要的核心函数
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();
}
for (; ; )循环里,不断的获取MessageQueue里面的Message,不断的对message进行处理,这里我们看msg.target.dispatchMessage(msg)而这里的msg.target正好是个handler所以我们回过头来看看handler的dispatchMessage函数
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
直接回调了public void handleMessage(Message msg) {}函数,这个函数我们再熟悉不过也就是我们写处理的地方,现在让我们理清整个流程
首先我们new Handler->然后Handler会new 一个looper->looper又会生成唯一的MessageQueue进行绑定->当looper开始工作时他会查询MessageQueue->当MessageQueue有消息时又会回调handler的处理函数
那么我们就可以自己来写一个消息队列了
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.loop()之后的代码不会被立即执行,当调用后 mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。Looper对象通过MessageQueue 来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。
这样你的线程就具有了消息处理机制了,在Handler中进行消息处理。参考
在了解了handler的基本流程后我们来看看android中怎样用handler来与主线程进行通信
主线程Handler的生成
Android中的线程主要在ActivityThread.Java中,程序入口public static void main(String[] args)
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
Activity在启动的时候直接生成了一个Looper将主线程与此绑定,在末尾开启了消息查询,所以当我们在Activity里声明了一个Handler变量它所处的当前线程就是Activity的主线程(Thread.currentThread()),也就获得了主线程的消息队列,因为UI是属于主线程的资源和我们当前的handler属于同一作用域里,自然我们可以在主线程的MQ的回调里去操作UI
泄漏问题
上厕所接了个面试好紧张,问到泄漏问题,由于自己没接触过,也被虐了番,赶紧做记录,原来只是停留在使用,许多细节未发现,非常感谢那位面试官啊,仔细想想确实有些未发现的细节,在ActivityThread中虽然做了Handler的初始化,保证了我们后面new的Handler获得同一个looper,但是activity收尾的时候是没有销毁handler,万一这时候MessageQueue里还有任务未完成,那么Activity是持有handler的引用的,因此activity就无法销毁,导致持有的activity资源泄漏,虽然平时使用都会在onDestroy里置空Handler,确实没考虑为啥会泄漏的问题,当然工作线程的就没这个问题
既然知道了原因,那么我们来看看解决方案。
声明Handler为static类;在外部类中实例化一个外部类的WeakReference(弱引用)并且在Handler初始化时传入这个对象给你的Handler;将所有引用的外部类成员使用WeakReference对象。
下次有机会测试看看