Handler是Android中用于帮助将子线程的数据传递给主线程的,同时也可以实现任意两个线程间的数据传输
将信息传递给主线程
用法
平时我们最常用handler进行传输消息的方式是: 在主线程中新建一个handler
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case MSG_WHAT:
break;
}
}
};
然后在任意线程中发送消息给主线程的handler
private void sendMessageToMainThreadByWorkThread(){
new Thread(){
@Override
public void run() {
super.run();
Message message = handler.obtainMessage(MSG_WHAT);
message.obj = "hello";
handler.sendMessage(message);
}
}.start();
}
Message属性
| 属性 | 用途 |
|---|
| Message.what | 一个用于标记信息来源的int值 | | Message.arg1,Message.arg2 | 在Message初定义时用来传递int类型的两个变量 | | Message.obj | 用于传递任何实例化的对象 |
传输过程
重写主线程中创建的Handler的handlerMessage()方法,通过sendMessage()发送出去的Message会在Handler线程被接收并处理。
子线程间传递信息
private Handler handler;
private void tryHandler(){
Thread threadA = new Thread(){
@Override
public void run() {
super.run();
Looper.prepare();
handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Log.d("ThreadA.out","threadA receiver message: " + ((String) msg.obj));
}
};
Looper.loop();
}
};
Thread threadB = new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
Message message = handler.obtainMessage();
message.obj = "hello ThreadA from ThreadB!";
handler.sendMessage(message);
}
};
threadA.start();
threadB.start();
}
运行后看到了接收消息成功的日志  在代码中,标记了两个注意点。 如果尝试将注意点1的Looper.prepare();删去的话,运行就会报错; 如果尝试将注意点2的Looper.loop();删去的话,线程A就接收不到消息。
Looper.prepare()
通过查看Looper.java源码可知,prepare()方法是在当前线程里新建了一个Looper() ThreadLocal用于维护线程的本地变量  然后在Looper的构造函数中,新建了一个MessageQueue()对象,用于存放Message  在创建主线程的时候Android已经调用了Looper.prepareMainLooper()和Looper.loop()方法,所以在主线程中可以直接创建Handler并使用
发送消息的过程
查看handler.java源码  可以看到,无论调用哪一种发送消息的方法,最后都会调用到sendMessageDelayed()方法。  而这个方法又调用了sendMessageAtTime()方法。  
查看这个方法可以发现,发送消息方法的关键在于MessageQueue的enqueueMessage()方法  可以看到,Message以链表的形式存储在了MessageQueue中,可以保证Message的时序性。
Looper.loop()
查看Looper.loop()源码  可以看到,蓝框中的for循环在不断的从looper内的MessageQueue中取出Message。  (源码较长,截取部分)  在成功得到Message对象后,通过Message的target调用dispatchMessage()方法分发消息 target就是我们创建的handler
分发消息的过程
查看Handler.java  如果我们已经设置了callback(Runnable对象),则直接调用该方法  如果未设置的话,则会调用mCallback.handleMessage()方法,即handler的handleMessage(),该方法会执行在主线程中,因为handler创建在主线程中。
总结
- 在使用
handler时,在handler所创建的线程维护一个唯一的Looper对象。 每个线程对应一个Looper,通过ThreadLocal来保证。 每个Looper对象内部维护唯一一个MessageQueue。 一个线程可以有多个handler,但只能有一个looper和一个MessgaeQueue。 Message在MessgaeQueue中是通过链表的形式存在的,即Message.nextLooper对象通过loop()开启了一个死循环,不断的从looper内的MessageQueue中取出Message,通过handler想消息分发给handler所在的线程。
补充
内存泄漏问题
Handler在使用过程中最需要注意的是内存泄漏问题 首先,Handler使用是用来进行线程间通信的,所以新开启的线程是会持有Handler引用的,如果在Activity等中创建Handler,并且是非静态内部类的形式,就有可能造成内存泄漏。非静态内部类是会隐式持有外部类的引用,所以当其他线程持有了该Handler,线程没有被销毁,则意味着Activity会一直被Handler持有引用而无法导致回收。同时,MessageQueue中如果存在未处理完的Message,Message的target也是对Activity等的持有引用,也会造成内存泄漏。
解决方法:
- 使用静态内部类+弱引用的方式
静态内部类static class TextHandler extend Handler{} 弱引用private WeakReference<Activity> textActivity; - 在外部类对象被销毁是清空MessageQueue中的消息。
handler.removeCallbackAndMessages(null)
Message缓存池
回收的Message不会被销毁而是放在缓存池中,要获取新的Message时优先从缓存池中获取容器,缓存池中为null时才会新建,这就是obtian()方法的作用。
|