Andriod异步消息处理机制解析


Handler 是Andriod 中一个非常重要的组成部分,使用场景:开了另外一个线程去网上下载东西,下载好了之后更新界面上的某个控件,但是UI控件不能在非UI线程上进行更新,所以使用handler,在handler的handleMessage中进行更新,在子线程中需要更新的时机上写sendMessage 就可以了,好了要实现这个需求,几句代码就可以完成需求了,但是写完之后,你会奇怪,为什么这样写就可以了呢?中间发生了什么呢?

要讲到底这中间发生了什么了,就需要从长计议了:

1 三足鼎立
首先介绍Looper 对象,它属于一个线程,从它的构造方法中,我们可以看到建立了一个 MessageQueue,结合sendMessage()的方法,我们很容易猜测,Message都将在这里收集排队;还有三个函数是比较重要的,Looper.prepare(),获得looper对象,Looper.loop() 对消息队列进行监控,就是对messagequeue进行出队,Looper.quit() 停止对消息队列的循环。整个异步消息机制就在looper ,handler ,MessageQueue三者的协作下进行的,handler负责把各个地方的消息收集起来发送给MessageQueue, 这样MessageQueue里面就承载着所有待处理的消息,   looper是幕后老大,监控着MessageQueue,负责将消息取出来,并通过msg.target.dispatchMessage(msg)函数进行处理,而dispatchMessage函数里面赫然是handle的handleMessage和callback,callback 执行的是在handler通过new Thread()形式来实现功能的机制(如 runOnUiThread函数其实,实质上就是这样的,调用 handler.post(new Runnable())) ,如此以来,handler里面的handlerMessage 方法就得到了执行,而且是在主线程中得到了执行。

<强烈想画一张三者的图,后续补上>

2 子线程中的handler
 通过以上解释,我们知道,要实现整个异步详细处理机制,是需要三个对象的协作,一个从属于主线程的looper对象,一个由looper对象建造出来的MessageQueue,一个或多个handler 进行Message的发送和handleMessage具体功能的编写,那么如此以来,要与某个线程通信,就可以通过handler来实现,比如要实现主线程和另外一个子线程之间的通信,<好遗憾,博主还没有遇到这样的需求,十分想要在实际中运用一把>,那么就要在子线程中建立这样的三足鼎立,这个时候问题就来了,子线程里面直接new handler,好像报错了,其原因就是主线程的Looper对象在ActivityThread中已经封装好了,并且是通过ThreadLocal进行管理的,这后面还有好多事,先放着再研究,那么你自己new 的childThread是没有looper对象的,所以需要你显式地写出来,则大体是这样的:
   
class childThread extends Thread{
 Handler mChildHandler;
 public void run(){
  Looper.prepare();
  mChildHandler = new Handler(){
   public void handleThread(Message msg){
    // do the things that you want
   }
  };
  Looper.loop();
 
 }
  // 在其他类中停止时需要通过handler的getLooper方法得到looper对象
 public void stopLoop(){
  mChildHandler.getLooper().quit();
 }
}

3 handlerThread
Android官方还有一个handlerThread的类,其实就是如果你需要一个可以通过handler进行消息机制的thread的时候,你可以使用handerThread ,在这里给你封装了looper对象,你只需要初始化的时候传入一个识别改线程的名字即可,可以学习一下handlerThread的源码,里面有关于同步的知识,毕竟handler是Android中的线程之间的通信方式之一,那么java之间的线程通信方式则是一切的基础,synchronize,  notifyAll()   wait()等方法,那还有什么其他的通信方式么,进程间的通信方法是什么?管道,信号量,共享内存。。。额,扯远了。

4 有关于handler的内存泄露
因为在使用handler的时候,我们往往使用了内部匿名类的方式来创建,而这种方法恰恰因为内部匿名类对外部类持有引用对象, <这可是java的基础哦,以前我还真不知道>,若内部类里有一直无法释放的对象就会导致外部对象无法释放使得GC不能将其回收的内存泄露,那解决方法如下:
1) 将handler里面的会一直运行下去的功能停掉,如下载线程等,将其停掉;removeCallback() removeMessage()
2)将handler声明为静态类,不过这种方法还要另外给handler写个类,同时还要通过构造方法将activity对象传递给当前类的一个弱引用对象,要多麻烦有多麻烦。。。。不过好像知道这样的方法感觉很牛逼。。。功力又上升了一层的感觉,好吧,是我自我感觉良好,大牛肯定嗤之以鼻。

5 总结
目前自己用到的handler实现的功能有:
1)  开机动画,延迟启动,利用了handler.postDelayed(startActvity(),Time);
2)   每秒钟刷新手表的界面,使手表显示钟表走动的界面,在handleMessage() 里面调用 postinvalidate() 刷新界面
3) 线程开启之后产生结果通过handler刷新UI。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值