Android学习笔记——内存泄漏

苏昊英
2023-12-01

参考网上别人的文章,做一下内存泄漏情况的总结及解决方法

单例模式造成的内存泄漏

如果单例的成员中有Context的话,由于单例在生成后就一直存在于内存中,所以其持有的Context对象不会被回收,即Activity很难会被回收。
可以让其持有Application的Context对象即可:

public class SingleInstanceTest {
    private static SingleInstanceTest sInstance;
    private Context mContext;
    private SingleInstanceTest(Context context){
        this.mContext = context.getApplicationContext();
    }
    public static SingleInstanceTest newInstance(Context context){
        if(sInstance == null){
            sInstance = new SingleInstanceTest(context);
        }
        return sInstance;
    }
}

另外,当某些类需要持有Context时,也可能会出现和上述类似的情况,这时可以使用弱引用来规避这一情况:

public class Sample {

    private WeakReference<Context> mWeakReference;

    public Sample(Context context){
        this.mWeakReference = new WeakReference<>(context);
    }

    public Context getContext() {
        if(mWeakReference.get() != null){
            return mWeakReference.get();
        }
        return null;
    }
}

内部类/匿名类

当Activity中有非静态内部类或者匿名类的对象时,这些对象会持有该Activity对象的引用,如果内部对象的存活时间很长,会导致Activity无法被回收。
解决方法是将其以静态内部类的形式重写或者使用弱引用
适用于Thread Handler AsyncTask等耗时工具
使用静态内部类:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new MyAscnyTask().execute();
    }

    static class MyAscnyTask extends AsyncTask<Void, Integer, String>{
        @Override
        protected String doInBackground(Void... params) {
            try {
                Thread.sleep(50000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "";
        }
    }
}

handler引起的内存泄漏:
第一种解决方法 使用静态内部类以及弱引用

//自定义handler
public static class HandlerHolder extends Handler {
    WeakReference<OnReceiveMessageListener> mListenerWeakReference;
    /**
     * @param listener 收到消息回调接口
     */
    HandlerHolder(OnReceiveMessageListener listener) {
        mListenerWeakReference = new WeakReference<>(listener);
    }

    @Override
    public void handleMessage(Message msg) {
        if (mListenerWeakReference!=null && mListenerWeakReference.get()!=null){
            mListenerWeakReference.get().handlerMessage(msg);
        }
    }
}

//创建handler对象
private HandlerHolder handler = new HandlerHolder(new OnReceiveMessageListener() {
    @Override
    public void handlerMessage(Message msg) {
        switch (msg.what){
            case 1:
                TextView textView1 = (TextView) msg.obj;
                showBottomInAnimation(textView1);
                break;
            case 2:
                TextView textView2 = (TextView) msg.obj;
                showBottomOutAnimation(textView2);
                break;
        }
    }
});

//发送消息
Message message = new Message();
message.what = 1;
message.obj = textView;
handler.sendMessageDelayed(message,time);

第二种解决方法:移除消息队列中所有的消息

@Override
protected void onDestroy() {
    super.onDestroy();
    if(handler!=null){
        handler.removeCallbacksAndMessages(null);
        handler = null;
    }
}

非静态内部类创建静态实例造成的内存泄漏

private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if(mResource == null){
        mResource = new TestResource();
    }
}

class TestResource {
     //里面代码引用上下文,Activity.this会导致内存泄漏
}

资源未关闭/未注销造成的内存泄漏

BroadcastReceiver,ContentObserver,FileObserver,Cursor,Callback等在 Activity onDestroy 或者某类生命周期结束之后一定要 unregister 或者 close 掉否则这个 Activity 类会被 system 强引用,不会被内存回收。值得注意的是,关闭的语句必须在finally中进行关闭,否则有可能因为异常未关闭资源,致使activity泄漏
这样就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。

参考:
https://github.com/yangchong211/YCBlogs/blob/master/android/优化相关/01.内存泄漏优化.md
https://www.jianshu.com/p/65f914e6a2f8

 类似资料: