Handler实现

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

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

MessageQueue没什么好讲的主要是Looper的机制,需要介绍下,Looper里面有一个比较重要的属性ThreadLocal<Looper>,需要简单介绍一下。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对象。
下次有机会测试看看

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容