引言
先来看一段代码
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
这里初始化Handler对象,存在几个问题:
- 1、内存泄漏
- 2、new Handler() 无参构造方法,已被废弃(我看的android-31源码)
@deprecated
Default constructor associates this handler with the {@link Looper} for the current thread. If this thread does not have a looper, this handler won’t be able to receive messages so an exception is thrown.
默认构造函数将此处理程序与当前线程的{@link Looper}相关联。如果这个线程没有looper,那么这个处理程序将无法接收消息,因此会引发异常。
- 3、如果在子线程,需要指定Looper对象,否则会在执行handleMessage()时崩溃
内存泄漏
不能正确处理好Handler的生命周期,将会导致内存泄漏:
- (1)handler被重复创建:如果每次使用都重新创建handler实例,但没有及时删除,会导致handler的数量不断增加,最终耗尽系统资源并导致内存泄漏。
- (2)handler引用外部对象:
- 如果handler在回调函数中引用了外部对象,而这些对象本身也在handler的作用域之外,则这些对象将无法被垃圾回收器回收,从而导致内存泄漏。
- Handler是与当前线程的消息队列相关联的。如果在某些情况下,消息队列中仍有未处理的消息时,由于持有对Handler的引用,它可能会阻止Handler及其关联的对象被垃圾回收,从而导致内存泄漏。
比如, Handler 是非静态内部类的实例,它会隐性持有对所在的外部类的引用(如HandlerActivity)。那么,当 HandlerActivity关闭,而Handler中的消息还没有处理完时,handler会一直持有 HandlerActivity 实例,而无法被回收。
解决:
1、使用公共变量,避免重复创建对象;
2、初始化Handler使用静态内部类,并且持有的对象使用弱引用。
因为静态内部类不会持有对外部类的引用,且生命周期与外部类相同。当外部类实例被回收后,静态内部类实例也会被回收,这样就避免内存泄漏。
3、Handler持有的对象使用弱引用:WeakReference
4、使用结束后,移除MessageQueue中的消息:removeCallbacksAndMessages
Looper轮询器
上源码
class Looper {
// 保存当前线程的Lopper单例对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue; // 消息队列--单链表结构
final Thread mThread; // 当前线程
// 初始化Looper单例对象
private void prepare() {
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* new Looper() 初始化消息队列和当前进程
* /
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); // 初始化消息队列
mThread = Thread.currentThread(); // 获取当前线程
}
/**
* 应用启动时,主线程入口类main()方法会调用此方法创建主线程的Looper
* /
public static void prepareMainLooper() {
prepare(false);
sMainLooper = myLooper();
}
/**
* 启动消息循环,发送消息。这个方法会一直阻塞当前线程
* /
public static void loop() {
...
loopOnce(me, ident, thresholdOverride)
}
private static boolean loopOnce(final Looper me, ...) {
Message msg = me.mQueue.next(); // 从队列获取消息
msg.target.dispatchMessage(msg); // 分发消息
}
/**
* 停止循环,以确保线程能够正常退出
* 其实是调用了MessageQueue的removeAllMessagesLocked()方法
* 它会清空消息队列
* /
public void quit() {
mQueue.quit(false); // -> MessageQueue # removeAllMessagesLocked()
}
}
MessageQueue 中两个方法的区别:
- removeAllMessagesLocked():清空消息队列中的所有消息
- removeCallbacksAndMessages():同时移除与指定对象相关的所有回调和消息
-
子线程里使用Handler使用,需要指定Looper对象
当你在子线程中创建一个Handler对象时,默认情况下它是与该子线程没有Looper相关联的。因此,调用handleMessage方法时会发生崩溃。
因此,需要先初始化Looper对象,然后再创建Handler对象,以便让Handler能够与该Looper所在线程的消息队列进行交互。 -
主线程里创建Handler时,可以不用显式指定Looper对象
由于主线程已经默认存在一个Looper对象,因此我们可以直接使用Handler()构造函数来创建Handler对象,而无需指定Looper参数。
主线程什么时候默认创建了Looper对象?
- 在应用程序启动时就被创建并关联到主线程上。这样,在主线程中执行UI操作时,就可以直接使用已经存在的Looper对象来处理消息循环。但是,如果某个子线程需要执行异步任务并且需要处理消息循环,则必须手动调用Looper.prepare()和Looper.loop()来创建Looper对象并启动消息循环。
// 主线程入口类
public final class ActivityThread {
public static void main(String[] args) {
// 1.新建Looper对象、MessageQueue对象
Looper.prepareMainLooper(); // prepare() - new Looper(quitAllowed)
// 2、轮询器,不断获取消息 Looper.loop();
Looper.loop();
}
}
写法示例1-主线程
// 初始化Handler对象
private MyHandler myHandler = new MyHandler(this);
// 静态内部类 MyHandler
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mContext; // 弱引用
MyHandler(Context context) {
this.mContext = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Context context = mContext.get();
// 消息处理
}
}
// 发送消息
private void sendMsg() {
Message message = Messge.obtain();
message.what = 1;
message.obj = "Hello, World!";
myHandler.sendMessage(message);
myHandler.sendMessageDelayed(message, 1000); // 延迟1秒后发送消息
myHandler.postDelayed(new Runnable() {
@Override
public void run() {
// 执行延迟执行的任务
}
}, 2000); // 延迟2秒后执行任务
}
@Override
protected void onDestroy() {
super.onDestroy();
if (myHandler != null) {
// 发送多个延迟消息时,建议退出前移除消息,清除关联的对象
myHandler.removeCallbacksAndMessages(null);
}
}
要点:静态内部类、弱引用、移除消息
写法示例2-子线程
public class MyThread extends Thread {
private static class MyHandler extends Handler {
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 处理消息
}
}
@Override
public void run() {
// 初始化Looper
Looper.prepare();
// 初始化Handler对象,并为Handler显式指定Looper对象
Handler handler = new MyHandler(Looper.myLooper());
// 使用 handler 对象发送消息
Message message = Message.obtain();
message.what = 1; // 设置消息标识
message.obj = "Hello, World!"; // 设置消息内容
handler.sendMessage(message);
// 启动Looper
Looper.loop();
// 退出Looper循环
Looper.myLooper().quit();
handler.removeCallbacksAndMessages(null);
}
}
要点:
1、新建Handler对象,使用静态内部类,避免内存泄漏
2、使用Handler之前,初始化Looper对象:Looper.prepare()
3、为Handler显式指定Looper对象,确保它在正确的线程中运行,并能够正确地处理消息
4、调用Looper.loop()方法启动消息循环,发送消息。这个方法会一直阻塞当前线程。
5、调用Looper.myLooper().quit()停止循环,以确保线程能够正常退出。