内存泄露的概念:强引用所指向的对象不会被回收,可能导致内存泄漏,虚拟机宁愿抛出OOM也不会去回收他指向的对象;意思就是你用资源的时候为他开辟了一段空间,当你用完时忘记释放资源了,这时内存还被占用着,一次没关系,但是内存泄漏次数多了就会导致内存溢出。
以下是我在项目中遇到的三种内存泄露的情况,逐个分析给出解决方案。
1. 单例的使用导致内存泄露:
Android的单例模式在我们项目开发中经常会用到,不过使用的不恰当的话也会造成内存泄漏。因为单例的静态特性使得单例的生命周期和应用的生命周期一样长, 这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。
Android中习惯使用单例的常见类: xxxManager , xxxHelper , xxxUtils 等。
方案1.png
将传入的Context对象替换为context.getApplicationContext(),使我们单例的生命周期和应用的一样长,这样就防止了内存泄漏,这也是最好的解决方式。
或者使用软引用,如果需要使用相应的context对象,只需要使用 wr.get()即可,代码如下:
2.使用webView导致的内存泄露
我们在创建webView对象时,尽量上下文变量使用applicationContext:
webView = new WebView(getApplicationContext());
webView.getSettings().setJavaScriptEnabled(true);
另外记住当activity生命周期结束时及时销毁/释放资源:
@Override
protected void onDestroy() {
if (webView != null) {
ViewParent parent = webView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(webView);
}
webView.stopLoading();
webView.getSettings().setJavaScriptEnabled(false);
webView.clearHistory();
webView.removeAllViews();
webView.destroy();
}
super.onDestroy();
}
3.使用handler导致的内存泄露:
①先说handler导致activity内存泄露的原因
handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),我们知道内部类天然持有外部类的实例引用,这样在GC垃圾回收机制进行回收时发现这个Activity居然还有其他引用存在,因而就不会去回收这个Activity,进而导致activity泄露。
②为何handler要定义为static?
因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露
③为何handler要定义为static的同时,还要用WeakReference 包裹外部类的对象?
这是因为我们需要使用外部类的成员,可以通过"activity. "获取变量方法等,如果直接使用强引用,显然会导致activity泄露。
解决内存泄露:
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();
}
}
需要特别注意Context的使用:
Context的正确姿势:
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
网友评论