Android Handler
1. handler
handler 是一个消息分发对象。handler 是android提供的用来更新UI和处理消息的机制。使用它最本质的原因是,为了解决多线程并发问题。所有更新UI的操作,都是在主线程的消息对列中轮询。
handler 将消息发给Looper管理的MessageQueue,并负责处理Looper分发给它的消息。
2. handler、Looper、Message三者关系
这3个都与异步消息处理有关。
异步消息处理线程启动之后,会进入一个无限的循环体中。每循环一次,从其内部的消息对列中取出一个消息,然后回调相应的消息处理函数,执行完一个消息后继续循环。若消息对列为空,线程则会阻塞等待。
android 消息机制主要是指Handler的运行机制,Handler运行需要MessageQueue和Looper支撑。MessageQueue采用的是单链表结构,Looper则是消息循环。由于MessageQueue只是一个消息存储单元,无法去处理消息,而Looper就是专门处理消息的,它会一直去循环查找是否有新消息,若有,则处理;若无,则等待。
Handler创建时会采用当前线程的Looper 来构造消息循环系统,线程默认是没有Looper的,若需要使用Handler,就必须为线程创建Looper,默认的UI线程ActivityThread,,它被创建的时候就会初始化Looper,因此在主线程中默认可以使用Handler。
3. handler类的方法
void handleMessage( Message msg):处理消息。该方法常用于被重写。
final boolean hasMessages( int what) : 检查消息队列中是否含有what 属性为指定值的消息。
final boolean hasMessages( int what ,Object object) :检查消息队列中是否含有what 属性为指定值且Object属性为指定对象的消息。
多个重载的Message obtainMessage():获取消息。
sendEmptyMessage( int what):发送空消息。
final boolean sendEmptyMessageDelayed( int what, long delayMillis) :指定多少毫秒后发送空消息。
final boolean sendMessage( Message msg) :立即发送消息。
final boolean sendMessageDelayed( Message msg, long delayMillis):指定多少毫秒后发送消息。
Message: Handler 接收和处理的消息对象。其属性如下:
2个整型数值;
1个Object对象;
replyTo: 线程通信时使用;
what: 用户自定义消息码,让接收者识别消息。
MessageQueue:Message 的队列。采用先进先出管理Message,每个线程最多可以拥有一个。
Looper: 消息泵,MessageQueue的管理者,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。
每个线程只有一个Looper;
Looper.prepare() :为当前线程创建Looper对象;
Looper.myLooper():获取当前线程的Looper对象;
Handler:能把消息发送给MessageQueue,并负责处理Looper分给它的消息。
4. 如何发送消息
a. 使用Thread发送消息
b. 使用Runnable发送消息
5. 注意的点
非静态内部类容易引起内存泄漏:
- 当一个Android应用启动的时候,会自动创建一个供应用主线程使用的Looper实例。Looper的主要工作就是一个一个处理消息队列中的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息中,然后加入到Looper要处理的消息队列中,由Looper负责一条一条地进行处理。主线程中的Looper生命周期和当前应用一样长。
- 当一个Handler在主线程进行了初始化之后,我们发送一个target为这个Handler的消息到Looper处理的消息队列时,实际上已经发送的消息已经包含了一个Handler实例的引用,只有这样Looper在处理到这条消息时才可以调用Handler#handleMessage(Message)完成消息的正确处理。
- 在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用。
解决的方式:
要解决这种问题,思路就是不适用非静态内部类,继承Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。另外关于同样也需要将Runnable设置为静态的成员属性。注意:一个静态的匿名内部类实例不会持有外部类的引用。