这节介绍Handler类使用相关的知识(以下分析都是基于android 12代码)
1. Handler的使用
1.1 创建Handler实例
创建Handler实例直接调用相应的构造函数即可,如下:
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback) {
this(callback, false);
}
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler类提供了各种重载的构造方法,现介绍下它的几个属性:
- mLooper:如果构造方法中传递的looper为null,则直接调用Looper.myLooper方法获取线程对应的Looper,不存在则抛异常
- mQueue:l类型MessageQueue
- mCallback:类型为Callback,设置了这个属性,在dispatchMessage方法中会先执行它的回调
- mAsynchronous:若它的值为true,代表通过Handler创建的Message都是异步的
1.2 创建Message的方法
Handler提供了创建Message的方法,如下:
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
上面只贴了一部分,主要是为了更方便的创建Message,Handler提供了这些重载方法。
1.3 执行Runnable
Handler提供了执行Runnable的方法,如下:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
如上面,Handler可以直接post一个Runnable,还可以delay一段时间后执行Runnable,还可以在某个时间点执行Runnable
1.4 延迟或者在某个时间点执行Message
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
上面介绍了Handler提供的功能,但是如果在使用的时候,如果使用不当有可能会出现内存泄漏的问题,那下面就来介绍下相关的内容
2. Handler使用不当与内存泄漏
内存泄漏
一个对象本应该在它“生命结束”后,被垃圾回收器回收掉。但是由于一个生命周期更长的对象引用了它,导致垃圾回收器无法完成回收。它作为一个“毫无用处”的对象,占用着“无辜”的内存。
造成内存泄漏原因分析
下面是一段Handler使用不当,有可能会造成内存泄漏的代码
public class MyActivity extends Activity{
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1 ){
}
}
}
protected void onResume() {
super.onResume();
handler.postDelayed(new Runnable(){
省略业务代码
},10);
}
}
上面的代码在Activity的onResume方法中,在10s后去执行一个Runnable,这段代码是比较容易出现内存泄漏的,但是并不是说一定会出现。
在这样的场景下会出现:Activity的onResume方法被执行后,这时候用户刚好要退出Activity,那这种场景下必出现内存泄漏。
来分析下为啥上面的场景会出现内存泄漏:
- Handler handler的实例是一个匿名内部类,匿名内部类编译器会为它增加一个Activity类型的属性,同时它的构造函数增加一个参数,这个参数指向了当前的Activity对象,这样handler就持有了当前Activity对象。
- new Runnable的时候也是一个匿名内部类,它同样也持有当前的Activity对象
- 在调用handler.postDelayed这个方法的时候,最终会创建一个Message对象,这个Message的callback属性指向了上面创建的Runnable,同时它的target属性指向了handler。创建的Message对象会被放入MessageQueue的链表中,在10s后才能执行。
- 因为当前Activity被用户销毁了变成“无用”对象了,但是MessageQueue的链表却还间接的引用着当前的Activity,MessageQueue的生命周期与app进程是一致的,最终导致当前的Activity对象不能被即时回收
解决内存泄漏
如下代码:
public class MyActivity extends Activity{
private Handler handler = new MyHandler(this);
private static class MyHandler extends Handler{
private WeakReference<Activity> activity;
MyHandler(Activity activity) {
this.activity = new WeakReference<>(activity);
}
}
private Runnable runnable = new Runnable(){
省略业务代码
};
protected void onResume() {
super.onResume();
handler.postDelayed(runnable,10);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(runnable);
}
}
如上代码可以解决Handler使用不当,导致有可能出现内存泄漏问题:
- 把Handler类的子类定义为静态内部类,使用弱引用来引用Activity对象
- 在onDestory的方法中,调用handler的removeCallbacks方法,把runnable从MessageQueue的链表中移除掉
总结
到此关于Handler的介绍就结束了,关于handler机制系列的文章也介绍完了,欢迎关注其他系列文章。