博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android内存泄漏的常见场景及解决方案
阅读量:6506 次
发布时间:2019-06-24

本文共 5245 字,大约阅读时间需要 17 分钟。

hot3.png

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

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 类,在构造方法中,我传入一个Context。Context 包括Application\Activity\Service,如果我传入的是Application,那么不会有什么影响,因为我所需要的时间是一整个程序的生命周期。

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

解决办法:

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

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

 

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 的时候,代码中的mHandler为Handler 的非静态内部类的实例,所以mHandler 持有对外部类,即Activity 的引用。并且Handler 中的Looper不断轮询消息队列中的message,message 又持有mHandler的引用,但mHandler又持有Activity 的引用,因此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
reference; public MyHandler(Context context) { // TODO Auto-generated constructor stub reference = new WeakReference
(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 设置为静态内部类。

 

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

public class ThreadActivity extends Activity {        public void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            new MyThread().start();        }        private class MyThread extends Thread {            @Override            public void run() {                super.run();                dosomthing();            }        }        private void dosomthing(){        }    }

假设MyThread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏, 

一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。 
由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时, 
MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。

这种线程导致的内存泄露问题应该如何解决呢?

  1. 将线程的内部类,改为静态内部类。
  2. 在线程内部采用弱引用保存Context引用。
  3. public class ThreadAvoidActivity extends Activity {            public void onCreate(Bundle savedInstanceState) {                super.onCreate(savedInstanceState);                setContentView(R.layout.activity_main);                new MyThread(this).start();            }            private void dosomthing() {            }            private static class MyThread extends Thread {                WeakReference
    mThreadActivityRef; public MyThread(ThreadAvoidActivity activity) { mThreadActivityRef = new WeakReference
    ( activity); } @Override public void run() { super.run(); if (mThreadActivityRef == null) return; if (mThreadActivityRef.get() != null) mThreadActivityRef.get().dosomthing(); // dosomthing } } }

    上面的两个步骤其实是切换两个对象的双向强引用链接 

    静态内部类:切断Activity 对于 MyThread的强引用。 
    弱引用: 切断MyThread对于Activity 的强引用

AsynTask 内部类会如何呢?

有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重, 

Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor, 
该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的, 
因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。

 

资源没有及时关闭

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

TypedArray、Bitmap等要及时进行recycle。

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

 

 

 

转载于:https://my.oschina.net/appleliu/blog/1632635

你可能感兴趣的文章
labview如何弹出提示窗口_LabVIEW开发者必读的问答汇总,搞定疑难杂症全靠它了!...
查看>>
hikariconfig mysql_HikariConfig配置解析
查看>>
mysql批量数据多次查询数据库_mysql数据库批量操作
查看>>
jquery 乱码 传参_jquery获取URL中参数解决中文乱码问题的两种方法
查看>>
JDBC_MySQL_jdbc连接mysql_MySQL
查看>>
mysql cte的好处_Mysql 8 重要新特性 - CTE 通用表表达式
查看>>
zcu106 固化_xilinx zcu106 vcu demo
查看>>
java ftpclient 代码_java后台代码ftpclient下载文件
查看>>
java数据库生成model_继承BaseModelGenerator 生成Model时添加数据库表字段 生成代码示例...
查看>>
matlab建立桌面图标,在ubuntu16.04上创建matlab的快捷方式(实现方法)
查看>>
smarty使用php代码,笑谈配置,使用Smarty技术_php
查看>>
oracle数据实际值限制,c# – Oracle数据库TNS密钥“数据源”的值长度超过了’128’的限制...
查看>>
silk v3 decoder php,解码转换QQ微信的SILK v3编码音频为MP3或其他格式
查看>>
linux不能访问80端口,lunux开放80端口(本地访问不了linux文件可能是这个原因)...
查看>>
android单位转换小程序,微信小程序中rpx与rem单位转换
查看>>
ps切图教程 android,PS前端切图完整教程
查看>>
HTML如何把输入框变成必填值,required输入框为必填项
查看>>
背锅侠逆袭之路
查看>>
演示:使用协议分析器取证IPv6的报文结构
查看>>
oracle 11gr2 rac中的4种IP解说
查看>>