问题
在Java中非静态内部类或者匿名内部类都是默认持有外部类的引用的。
当我们使用handler时,如果我们按照下述写法,则会提示一个错误:
var hh = object : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
}
}
则会看到编译器提示我们一个警告,主要意思如下:
由于handler定义为内部类,可能会阻止GC。如果handler的Looper或MessageQueue 非主线程,那么没有问题。如果handler的Looper或MessageQueue 在主线程,那么需要按如下定义:定义handler为静态内部类,当你实例化handler的时候,传入一个外部类的弱引用,以便通过弱引用使用外部类的所有成员
原因
handler发送的消息在当前的handler的消息队列中,如果此时activity finish掉落,那么消息队列的消息依旧会由handler进行处理,如果此时handler为非静态内部类,持有外部类Activity的引用,这样GC垃圾回收机制进行回收时发现这个Activity还有引用存在,因而就不会去回收这个Activity,进而导致activity泄漏。
非静态内部类持有了外部类的对象。
解决办法
- 将handler定义为static。
静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄漏。
- 使用WeakReference包裹外部类的对象。
静态内部类中需要使用外部类的成员,弱引用不会导致内存泄漏。
示例代码
public class MainActivity extends AppCompatActivity {
TextView mTextView;
MyHandler mMyHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.tv_handler);
mMyHandler = new MyHandler(MainActivity.this);
doInBackground();
}
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity) {
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity mainActivity = mWeakReference.get();
switch (msg.what) {
case 1:
if (mainActivity != null) {
mainActivity.mTextView.setText(msg.obj + "");
}
break;
default:
break;
}
}
}
private void doInBackground() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//模拟耗时操作
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//耗时操作完成,发送消息给UI线程
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "更新UI";
mMyHandler.sendMessage(msg);
}
}).start();
}
}
网友评论