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.next Looper 对象通过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()方法的作用。
|