Handler实现

要了解Handler的话,首先要了解一下Looper与MessageQueue机制

  • looper 是android里面的消息泵,不断的从MessageQueue内抽取消息
  • MessageQueue 存储消息的队列是一个链表

MessageQueue没什么好讲的主要是Looper的机制,需要介绍下,Looper里面有一个比较重要的属性ThreadLocal,需要简单介绍一下。ThreadLocal与Synchronized相比较,ThreadLocal是为了隔离共享冲突,不进行线程间通信,而synchronized则是为了解决多线程对相同资源的并发访问问题,进行线程通信,也就是说如果需要同步的资源就不应该使用ThreadLocal,这里需要注意的是以前ThreadLocal存放资源使用Map,JDK1.7后好像是使用一个数组

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对象。
下次有机会测试看看

你可能感兴趣的:(Handler实现)