造成 Android 内存泄漏的最根本原因就是生命周期较长的对象持有生命周期较短的对象的引用
通常内存泄漏 有以下几种情况
1.单例模式引用context 对象
这里有 Context 对象,如果 Activity 中调用 getInstance 方法并传入 this 时, 就持有了此 Activity 的引用,当退出 Activity 时,Activity 就无法回收,造成内存泄漏,所以应该修改它的构造方法:为context.getApplicationContext()
通过 getApplicationContext 来获取 Application 的 Context,让它被单例持有,这样退出 Activity 时,Activity 对象就能正常被回收了,而 Application 的 Context 的生命周期和单例的生命周期是一致的,所有再整个 App 运行过程中都不会造成内存泄漏。
public static LocalBroadcastManager getInstance(Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext()); //不要直接使用context
}
return mInstance;
}
}
也可以: onDestroy 中取消 单例的绑定
例子:
@Override
protected void onDestroy() {
super.onDestroy();
NastyManager.getInstance().removeListener(this);
}
2.非静态内部类造成的内存泄漏
public class MainActivity extends AppCompatActivity {
private static Test test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (test == null) {
test = new Test();
//由于静态对象 test 的生命周期和整个应用的生命周期一致,
//而非静态内部类 Test 持有外部类 MainActivity 的引用,
//导致 MainActivity 退出的时候不能被回收,从而造成内存泄漏
}
}
private class Test {
}
}
解决方案:
解决的方法也很简单,把 test 改成非静态,这样 test 的生命周期和 MainActivity 是一样的了,就避免了内存泄漏。或者也可以把 Test 改成静态内部类,让 test 不持有 MainActivity 的引用,不过一般没有这种操作。
3.Handler 或 Runnable 作为非静态内部类
handler 和 runnable 都有定时器的功能,当它们作为非静态内部类的时候,同样会持有外部类的引用,如果它们的内部有延迟操作,在延迟操作还没有发生的时候,销毁了外部类,那么外部类对象无法回收,从而造成内存泄漏
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Handler 和 Runnable 作为匿名内部类,都会持有 MainActivity 的引用
//而它们内部有一个 10 秒钟的定时器,如果在打开 MainActivity 的 10 秒内关闭了 MainActivity,
//那么由于 Handler 和 Runnable 的生命周期比 MainActivity 长,会导致 MainActivity 无法被回收,从而造成内存泄漏。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
}
}, 10 * 1000);
}
}
解决方案:
一般套路就是把 Handler 和 Runnable 定义为静态内部类,这样它们就不再持有 MainActivity 的引用了
public class MainActivity extends AppCompatActivity {
private Handler handler;
private Runnable runnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new TestHandler();
runnable = new TestRunnable();
handler.postDelayed(runnable, 10 * 1000);
}
//静态内部类
private static class TestHandler extends Handler {
}
//静态内部类
private static class TestRunnable implements Runnable {
@Override
public void run() {
Log.d(TAG, "run: ");
}
}
private static final String TAG = "MainActivity";
}
// onDestory 调用 handler 的 removeCallbacks 方法来移除 Message,这样不但能避免内存泄漏,而且在退出 Activity 时取消了定时器,保证 10 秒以后也不会执行 run
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(runnable);
}
特殊情况
如果 Handler 或者 Runnable 中持有 Context 对象,那么即使使用静态内部类,还是会发生内存泄漏:
public class MainActivity extends AppCompatActivity {
private Handler handler;
private Runnable runnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里 我们又让其持有 MainActivity .this的引用
handler = new TestHandler(this);
runnable = new TestRunnable();
handler.postDelayed(runnable, 10 * 1000);
}
//这是由于在 Handler 中持有 Context 对象,而这个 Context 对象是通过 TestHandler 的构造方法传入的,它是一个 MainActivity 对象,也就是说,虽然 TestHandler 作为静态内部类不会持有外部类 MainActivity 的引用,
//但是我们在调用它的构造方法时,自己传入了 MainActivity 的对象,从而 handler 对象持有了 MainActivity 的引用,handler 的生命周期比 MainActivity 的生命周期长,因此会造成内存泄漏,
private static class TestHandler extends Handler {
private Context context;
private TestHandler(Context context) {
this.context = context;
}
}
private static class TestRunnable implements Runnable {
@Override
public void run() {
Log.d(TAG, "run: ");
}
}
private static final String TAG = "MainActivity";
}
解决方案
使用弱引用的方式来引用 Context 来避免内存泄漏,代码如下:
public class MainActivity extends AppCompatActivity {
private Handler handler;
private Runnable runnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//使用 弱引用 的方式
handler = new TestHandler(new WeakReference<Context>(this));
runnable = new TestRunnable();
handler.postDelayed(runnable, 10 * 1000);
}
private static class TestHandler extends Handler {
private Context context;
private TestHandler(WeakReference<Context> weakContext) {
context = weakContext.get();
}
}
private static class TestRunnable implements Runnable {
@Override
public void run() {
Log.d(TAG, "run: ");
}
}
private static final String TAG = "MainActivity";
}
4.AsyncTask造成内存泄漏
Activity2 中创建了一个匿名类AsyncTask,匿名类和非静态内部类相同,会持有外部类对象,这里也就是activity,因此如果你在Activity里声明且实例化一个匿名的AsyncTask对象,则可能会发生内存泄漏,如果这个线程在Activity销毁后还一直在后台执行,那这个线程会继续持有这个Activity的引用从而不会被GC(垃圾回收机制)回收,直到线程执行完成
public class Activity2 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_2);
findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
//匿名类和非静态内部类相同,会持有外部类对象
new AsyncTask<String,Integer,String>(){
@Override
protected String doInBackground(String... params) {
try {
Thread.sleep( 6000 );
} catch (InterruptedException e) {
}
return "ssss";
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d( "mmmmmm activity2 " , "" + s ) ;
}
}.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "" ) ;
}
}
解决方案
自定义静态AsyncTask类AsyncTask的周期和Activity周期保持一致。也就是在Activity生命周期结束时要将AsyncTask cancel掉
public class AsyncTaskActivity extends AppCompatActivity {
private static MyTask myTask ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_asynctask);
findViewById( R.id.finish).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
myTask = new MyTask() ;
myTask.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "") ;
}
private static class MyTask extends AsyncTask{
@Override
protected Object doInBackground(Object[] params) {
try {
//模拟耗时操作
Thread.sleep( 15000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
return "";
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消异步任务
if ( myTask != null ){
myTask.cancel(true ) ;
}
}
}
5.Timer Tasks 造成内存泄漏
public class TimerActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_2);
findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
//开始定时任务
timer();
}
void timer(){
new Timer().schedule(new TimerTask() {
@Override
public void run() {
while(true);
}
},1000 ); // 1秒后启动一个任务
}
}
解决方案
在适当的时机进行Cancel。 TimerTask用静态内部类
public class TimerActivity extends AppCompatActivity {
private TimerTask timerTask ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_2);
findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
//开始定时任务
timer();
}
void timer(){
timerTask = new MyTimerTask() ;
new Timer().schedule( timerTask ,1000 ); // 1秒后启动一个任务
}
//采用静态内部类
private static class MyTimerTask extends TimerTask{
@Override
public void run() {
while(true){
Log.d( "ttttttttt" , "timerTask" ) ;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消定时任务
if ( timerTask != null ){
timerTask.cancel() ;
}
}
}
6.其他内存泄漏情况
其他一些情况参考 http://blog.csdn.net/qq_32969313/article/details/51669211
还有一些其他的会导致内存泄漏的情况,比如 BraodcastReceiver 未取消注册,InputStream 未关闭等,这类内存泄漏非常简单,只要在平时写代码时多多注意即可避免。
在 MVP 的架构中,通常 Presenter 要同时持有 View 和 Model 的引用,如果在 Activity 退出的时候,Presenter 正在进行一个耗时操作,那么 Presenter 的生命周期会比 Activity 长,导致 Activity 无法回收,造成内存泄漏:
在 onDestory 方法中把 presenter 中的 view 对象置为空就可以了:
@Override protected void onDestroy() {
super.onDestroy();
presenter.detachView();
}
public void detachView() {
view = null;
}
也就是在退出 Activity 的时候,让 Presenter 不再持有 Activity 的引用,避免了内存泄漏。