最近在项目中使用延时Handler做Splish页面的跳转。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(SplishActivity.this,MainActivity.class);
startActivity(intent);
finish();
}
},100);
谁知道产生如下内存泄露。
static android.app.Instrumentation.mRecommendActivity
referencs android.app.Instrumentation$RecommendActivity.mTarget
leaks XXX.SplishActivity instance
8D922718668811AB9F21445AB0017F90.jpg
仔细一想确实是这么回事,因为可能延时Handler还没处理消息,页面就finish了,但是因为Handler是非静态内部内,会持有外部Activity的引用,所以导致外部Activity不能被销毁,故而产生内存泄露。
所以为了安全起见,把Handler改为静态内部类,并且持有弱引用。
private Handler mHandler = new MyHandler(this);
private static class MyHandler extends Handler{
private final WeakReference<Activity> mActivity;
public MyHandler(Activity activity) {
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
System.out.println(msg);
if(mActivity.get() == null) {
return;
}
}
}
private Runnable mRunable = new Runnable() {
@Override
public void run() {
Intent intent = new Intent(SplishActivity.this,MainActivity.class);
startActivity(intent);
finish();
}
};
mHandler.postDelayed(mRunable,100);
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mRunable);
}
总结内部Handler类引起内存泄露的原因
1、当Android应用启动的时候,会先创建一个应用主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。
2、当在主线程中初始化Handler时,该Handler和Looper的消息队列关联。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。
3、在Java中,非静态(匿名)内部类会引用外部类对象。而静态内部类不会引用外部类对象。
如果外部类是Activity,则会引起Activity泄露 。
4、当Activity finish后,延时消息会继续存在主线程消息队列中,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。
5、要修改该问题,只需要按照Lint提示的那样,把Handler类定义为静态即可,然后通过WeakReference 来保持外部的Activity对象。
更正一下
上面
mHandler.postDelayed(mRunable,100);
上面用mRunable还是提示内存泄露。
改成sendMessageDelayed
mHandler.sendMessageDelayed(Message.obtain(), 100);
网友评论