当前位置: 首页 > news >正文

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

2019独角兽企业重金招聘Python工程师标准>>> 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<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 设置为静态内部类。

 

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

    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<ThreadAvoidActivity> mThreadActivityRef;
    
                    public MyThread(ThreadAvoidActivity activity) {
                        mThreadActivityRef = new WeakReference<ThreadAvoidActivity>(
                                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

相关文章:

  • Linux CentOS下安装、配置mysql数据库
  • virtualenv和virtualenvwrapper的安装和使用
  • 【Kafka】
  • Android动画实现详解
  • HDU 1824 Let#39;s go home (2-SAT判定)
  • Java Proxy 和 CGLIB 动态代理原理
  • Scrapy
  • python数据分析及展示(二)
  • 千里走单骑:02-北京到上海骑记--Day1.首日征程
  • VS2008设置快捷键Ctrl+W关闭当前打开的文本编辑器窗口
  • 人工智能概念第一股?即将在美国上市的Veritone是怎样一家公司
  • 从setTimeout-setInterval看JS线程
  • 2017 5月22日上午
  • 详解MySQL数据类型
  • JavaScript(二)-精简
  • 深入了解以太坊
  • express + mock 让前后台并行开发
  • IDEA 插件开发入门教程
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • JDK9: 集成 Jshell 和 Maven 项目.
  • leetcode-27. Remove Element
  • underscore源码剖析之整体架构
  • 大整数乘法-表格法
  • 思维导图—你不知道的JavaScript中卷
  • 在Unity中实现一个简单的消息管理器
  • 阿里云重庆大学大数据训练营落地分享
  • #mysql 8.0 踩坑日记
  • (2)(2.10) LTM telemetry
  • (2)Java 简介
  • (8)STL算法之替换
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (南京观海微电子)——COF介绍
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (循环依赖问题)学习spring的第九天
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转)visual stdio 书签功能介绍
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .net 托管代码与非托管代码
  • .NET 中创建支持集合初始化器的类型
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .NET成年了,然后呢?
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .net中调用windows performance记录性能信息
  • ?php echo $logosrc[0];?,如何在一行中显示logo和标题?
  • @Validated和@Valid校验参数区别
  • [ vulhub漏洞复现篇 ] ThinkPHP 5.0.23-Rce