Android内存泄漏的常见场景及解决方案

哲学老师说,看待事物无非是了解它是什么,为什么,怎么做

所以,首先,我们先了解一下什么是“内存泄漏”

 

摘自百度的一段话:用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。

是不是有点拗口,换一种说法,有天你去一家饭店吃饭,有个胖子吃完饭了,却霸占着一张桌子不走,然而现在一堆人等着吃饭,结果那死胖子等到饭店打烊了才离开。

在这个例子中,饭店的桌子就好比内存空间,那个胖子就是一个函数,吃饭就是所执行的事件。

这么说是不是好理解多了,现在,我们要做的就是赶走这个死胖子。

 

在探讨怎么处理内存泄漏的之前,我们先探讨一下为什么这些函数不能释放内存空间,内存泄漏我们要怎么取检测。

 

首先,我们需要有合适的工具来检测我们的内存使用情况

内存泄漏工具

推荐该博主写的利用 LeakCanary 来检查 Android 内存泄漏

 

现在,我们正式进入主题

我们来看看常见的内存泄漏的情况,及解决方案

因为静态变量造成的内存泄漏

 

单例模式应该是每个Android 开发者最熟悉的设计模式了,但是如果使用不当,在Android 应用中很可能会造成内存泄漏。举个例子


public class MyApplication {

	private static MyApplication application;
	private MyApplication(Context context){
	}
	public static MyApplication getApplication(Context context){
		if(application==null){
			application = new MyApplication(context);
		}
		return application;
	}
}

我创建了一个MyApplication 类,在构造方法中,我传入一个ContextContext 包括Application\Activity\Service,如果我传入的是Application,那么不会有什么影响,因为我所需要的时间是一整个程序的生命周期。

但是我如果传入的是Activity,那么,当我退出该Activity时,因为被static 修饰,单例对象持有对该Activity的引用,导致该Activity不会被回收,从而产生内存泄漏。

解决方案:

在获取单例的方法中,把原有的context 改成application = new MyApplication(context.getApplicationContext());

把传入的Context的生命周期设置成与Application 一样长,这样在使用单例的时候就和导入的Context 类型无关,防止了内存泄漏。

Handler 的错误使用

Handler是开发中异步处理最常使用到的工具之一,但是如果错误地使用Handler 也极容易产生内存泄漏。

举个栗子:

public class Act_test extends Activity{
	private static final int WHAT = 1;
	private Handler mHandler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case WHAT:
				//handle it
				break;
			default:
				break;
			}
		};
	};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Message message = Message.obtain();
		message.what = WHAT;
		mHandler.sendMessage(message);
		
	}
}

怎么样,看起来是不是合情合理,但是其中隐藏着一个重要的隐患,当我们创建出一个Handler 的时候,代码中的mHandlerHandler 的非静态内部类的实例,所以mHandler 持有对外部类,即Activity 的引用。并且Handler 中的Looper不断轮询消息队列中的messagemessage 又持有mHandler的引用,但mHandler这玩意儿又持有Activity 的引用,所以这下倒好,因为你一个message 导致我整个Activity都无法被回收,你说气人不气人。

解决方案:

public class Act_test extends Activity{
	private static final int WHAT = 1;
	private MyHandler mHandler = new MyHandler(this);
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Message message = Message.obtain();
		message.what = WHAT;
		mHandler.sendMessage(message);
		
	}
	static class MyHandler extends Handler{
		WeakReference<Context> reference;
		public MyHandler(Context context) {
			// TODO Auto-generated constructor stub
			reference = new WeakReference<Context>(context);
		}
		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			if(reference!=null){
				if(msg.what==WHAT){
				//handle it
				}
			}
		}
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		mHandler.removeCallbacksAndMessages(null);
	}
}

解释:首先,创建一个Handler 的静态内部类,这样,它将不再持有外部类的引用,并且将持有它的Context 进行弱引用,确保Activity 可以及时被回收。在前面分析过,Handler 内存泄漏是因为消息队列中还有未处理的Message ,所以当该Activity 被销毁时,将消息队列中的Message 清空即可。

非静态内部类的静态实例的错误使用

该错误与上例原因接近,先看错误代码:

public class Act_test extends Activity{

	private static Test mTest;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if(mTest==null){
			mTest = new Test();
		}
	}
	class Test{
		
	}
}

这种情况多使用于反复使用同一个Activity 为了避免重复创建资源。

但是这种写法同样存在着隐患,因为在该Activity中,我创建了一个Test 类的静态实例,每次启动Activity 都会使用该单例。但是由于非静态内部类持有外部类的使用,并且该非静态内部类又创建了一个静态的实例,这导致了该单例的存活时间与application的生命周期一样长,于是会一直持有该Activity的引用,导致其无法被回收。

解决方案:static class Test{

}

Test 设置为静态内部类。

 

不正确使用线程,造成内存泄漏

由于创建线程时用到了Runnable 为匿名内部类,持有对外部类的引用,但是如果该外部类在销毁之前,线程中还有未完成的任务,这将导致该外部类无法被回收。

解决方案:对其进行弱引用

 

资源没有及时关闭

在开发中,例如CursorFileIOStream等资源在使用后要及时进行关闭,避免造成内存的浪费。

TypedArrayBitmap等要及时进行recycle

EventBus BroadcastReceiver注册后避免重复注册,使用后及时销毁。

 

 


  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值