android中常见的内存溢出和解决办法
2015-12-29 15:10
267 查看
android中的内存溢出估计大多数人在写代码的时候都出现过,其实突然觉得工作一年和工作三年的区别是什么呢,其实干的工作也许都一样,产品汪看到的结果也都一样,那区别就是速度和质量了。
写在前面的一点儿想法:工作做完了其实不会的还有很多,每天都有莫名的危机感,从真正写代码的这一年多总觉得自己的学习速度比别人的慢很多
(无效的对象没有及时回收,导致内存不够用,致使程序
出错)
来个图片了解一下
知道为什么导致内存泄露那就很好办了,都是跟对象有关系(就是new出来的 不要想着他会跟你结婚)
主要有以下几方面吧:平时注意一下 完全可以杜绝的
Context
内部类(handler等)
Cursor
Adapter
Bitmap
看到这个图在稍加思索会不会觉得我们的工具类 貌似好多都持有了activity,而且工具类还是static类型。
在细琢磨一下呢。是不是activity的上下文都可以被application替代呢?
经验之谈:dialog ,fragment,inflate和启动activity 的上下文都是activity的,其他的都都可以被application替代。比如数据库的 服务的 广播的。都不要再用activity了吧。当然也要酌情处理。
举个栗子(太多了根本举不过来)
1.获取系统的服务
2.弄个数据库
其他的就是 千万不要在 static的工具类里面 添加activity上下文
简单说一下呢:比如 activity 开线程 跳刀handler 弹出dialog。 activity在销毁了,thread持有activity,然后跳到了handler,handler的对象也存在着了对吧,然后弹出dialog。这个时候呢dialog也持有了activity,但是因为activity销毁了所以不弹了。但对象还是持有者了。
怎么解决 内部类的问题呢。就是在 依附于的那个类销毁的时候 自己也销毁。就类似于activity销毁了。什么thread,dialog,handler全关掉
handler的情况
切断联系就行啦。
- dialog
刚才说的按个 activity都销毁了。但是还要在handler中执行dialog,那么久做个判断吧
thread 和asynctask的情况
在写这个的时候我就在想我还有必要写么。我都已经太久远的没用这两个了。我都用的是线程池了。
下面贴一个自定义的线程池代码,读者直接拿走(可以用 线程池+handler 或者 线程池+eventbus,rxbus等等来更新ui),activity销毁的事后 直接让线程池关闭就行啦
广播
… has leaked IntentReceiver … Are you missing a call to unregisterReceiver()?
这个错误我们现在公司的代码还有大把的了。这么解决,一个大神的方法
接下来的几个感觉大家应该都不会出太大的问题吧
也可以用lrucache等方法,这就不做详细介绍。
转载请注明:/article/3657723.html
就这样吧,忙里偷闲写了个博客,总结一下,其实内存溢出的问题都是 不小心导致的,避免起来也比较容易。文章有点儿长 读完了 :辛苦了您內,初级文章大神勿喷
写在前面的一点儿想法:工作做完了其实不会的还有很多,每天都有莫名的危机感,从真正写代码的这一年多总觉得自己的学习速度比别人的慢很多
内存溢出是什么鬼?
当某些对象不再被程序所使用,但是这些对象仍然被某些对象所引用着,进而导致垃圾收集器不能及时释放它们。(无效的对象没有及时回收,导致内存不够用,致使程序
出错)
来个图片了解一下
知道为什么导致内存泄露那就很好办了,都是跟对象有关系(就是new出来的 不要想着他会跟你结婚)
主要有以下几方面吧:平时注意一下 完全可以杜绝的
Context
内部类(handler等)
Cursor
Adapter
Bitmap
Context的溢出
来个图让大家分分钟理解一下:看到这个图在稍加思索会不会觉得我们的工具类 貌似好多都持有了activity,而且工具类还是static类型。
在细琢磨一下呢。是不是activity的上下文都可以被application替代呢?
经验之谈:dialog ,fragment,inflate和启动activity 的上下文都是activity的,其他的都都可以被application替代。比如数据库的 服务的 广播的。都不要再用activity了吧。当然也要酌情处理。
举个栗子(太多了根本举不过来)
1.获取系统的服务
[code]getSystemService(Context.SENSOR_SERVICE); 改为 getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
2.弄个数据库
[code] public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { /** * @param context 上下文 ; * @param name 数据库的名称 * @param cursorfactory 游标工厂null为默认的,是从第一条开始查询 * @param version 数据库的版本号从1开始。 */ public NoteSQLiteOpenHelper(getApplicationContext()) { super(getApplicationContext(), "note123.db", null, 1); } } 其实这里用activity的也无所谓但是会出现用着用着就把他弄成静态的了,那就悲剧了。
其他的就是 千万不要在 static的工具类里面 添加activity上下文
内部类的种种问题(感觉这个比较多一些呢)
简单说一下呢:比如 activity 开线程 跳刀handler 弹出dialog。 activity在销毁了,thread持有activity,然后跳到了handler,handler的对象也存在着了对吧,然后弹出dialog。这个时候呢dialog也持有了activity,但是因为activity销毁了所以不弹了。但对象还是持有者了。
怎么解决 内部类的问题呢。就是在 依附于的那个类销毁的时候 自己也销毁。就类似于activity销毁了。什么thread,dialog,handler全关掉
handler的情况
[code]public class LeakActivity extends Activity { /** * 声明静态内部类不会持有外部类的隐式引用 */ private static class MyHandler extends Handler { private final WeakReference<SampleActivity> mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference<SampleActivity>(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // ... } } } private final MyHandler mHandler = new MyHandler(this); /** * 这里的Runnable也是 * 声明静态内部类不会持有外部类的隐式引用 */ private static final Runnable sRunnable = new Runnable() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //10分钟之后发送消息 mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // 退回到上一个Activity finish(); } } @Override public void onDestroy() { //简单粗暴 mHandler.removeMessages(message); mHandler.removeCallbacks(Runnable); mHandler.removeCallbacksAndMessages(null); }
切断联系就行啦。
- dialog
刚才说的按个 activity都销毁了。但是还要在handler中执行dialog,那么久做个判断吧
[code]Handler handler = new Handler() { public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: if (!isFinishing()) { dialog.show(); } break; default: break; } } };
thread 和asynctask的情况
在写这个的时候我就在想我还有必要写么。我都已经太久远的没用这两个了。我都用的是线程池了。
下面贴一个自定义的线程池代码,读者直接拿走(可以用 线程池+handler 或者 线程池+eventbus,rxbus等等来更新ui),activity销毁的事后 直接让线程池关闭就行啦
[code]package com.ihealth.utils; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor.AbortPolicy; import java.util.concurrent.TimeUnit; /** * @author wanghao * * 说明:一个简易的线程池管理类,提供三个线程池 进本的 子线程操作都可以满足 * * 线程内部都是 挨个进行,只有创建多个线程池的才可能会并发进行。 */ public class ThreadManager { public static final String DEFAULT_SINGLE_POOL_NAME = "DEFAULT_SINGLE_POOL_NAME"; private static ThreadPoolProxy mLongPool = null; private static Object mLongLock = new Object(); private static ThreadPoolProxy mShortPool = null; private static Object mShortLock = new Object(); private static ThreadPoolProxy mDownloadPool = null; private static Object mDownloadLock = new Object(); private static Map<String, ThreadPoolProxy> mMap = new HashMap<String, ThreadPoolProxy>(); private static Object mSingleLock = new Object(); /** 获取下载线程 */ public static ThreadPoolProxy getDownloadPool() { synchronized (mDownloadLock) { if (mDownloadPool == null) { mDownloadPool = new ThreadPoolProxy(3, 3, 5L); } return mDownloadPool; } } /** 获取一个用于执行长耗时任务的线程池,避免和短耗时任务处在同一个队列而阻塞了重要的短耗时任务,通常用来联网操作 */ public static ThreadPoolProxy getLongPool() { synchronized (mLongLock) { if (mLongPool == null) { mLongPool = new ThreadPoolProxy(5, 5, 5L); } return mLongPool; } } /** 获取一个用于执行短耗时任务的线程池,避免因为和耗时长的任务处在同一个队列而长时间得不到执行,通常用来执行本地的IO/SQL */ public static ThreadPoolProxy getShortPool() { synchronized (mShortLock) { if (mShortPool == null) { mShortPool = new ThreadPoolProxy(2, 2, 5L); } return mShortPool; } } /** 获取一个单线程池,所有任务将会被按照加入的顺序执行,免除了同步开销的问题 */ public static ThreadPoolProxy getSinglePool() { return getSinglePool(DEFAULT_SINGLE_POOL_NAME); } /** 获取一个单线程池,所有任务将会被按照加入的顺序执行,免除了同步开销的问题 */ public static ThreadPoolProxy getSinglePool(String name) { synchronized (mSingleLock) { ThreadPoolProxy singlePool = mMap.get(name); if (singlePool == null) { singlePool = new ThreadPoolProxy(1, 1, 5L); mMap.put(name, singlePool); } return singlePool; } } public static class ThreadPoolProxy { private ThreadPoolExecutor mPool; private int mCorePoolSize; private int mMaximumPoolSize; private long mKeepAliveTime; private ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long keepAliveTime) { mCorePoolSize = corePoolSize; mMaximumPoolSize = maximumPoolSize; mKeepAliveTime = keepAliveTime; } /** 执行任务,当线程池处于关闭,将会重新创建新的线程池 */ public synchronized void execute(Runnable run) { if (run == null) { return; } if (mPool == null || mPool.isShutdown()) { //参数说明 //当线程池中的线程小于mCorePoolSize,直接创建新的线程加入线程池执行任务 //当线程池中的线程数目等于mCorePoolSize,将会把任务放入任务队列BlockingQueue中 //当BlockingQueue中的任务放满了,将会创建新的线程去执行, //但是当总线程数大于mMaximumPoolSize时,将会抛出异常,交给RejectedExecutionHandler处理 //mKeepAliveTime是线程执行完任务后,且队列中没有可以执行的任务,存活的时间,后面的参数是时间单位 //ThreadFactory是每次创建新的线程工厂 mPool = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new AbortPolicy()); } mPool.execute(run); } /** 取消线程池中某个还未执行的任务 */ public synchronized void cancel(Runnable run) { if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { mPool.getQueue().remove(run); } } /** 取消线程池中某个还未执行的任务 */ public synchronized boolean contains(Runnable run) { if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { return mPool.getQueue().contains(run); } else { return false; } } /** 立刻关闭线程池,并且正在执行的任务也将会被中断 */ public void stop() { if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { mPool.shutdownNow(); } } /** 平缓关闭单任务线程池,但是会确保所有已经加入的任务都将会被执行完毕才关闭 */ public synchronized void shutdown() { if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { mPool.shutdownNow(); } } } }
广播
… has leaked IntentReceiver … Are you missing a call to unregisterReceiver()?
这个错误我们现在公司的代码还有大把的了。这么解决,一个大神的方法
[code]简单粗暴 private void unregisterReceiverSafe(BroadcastReceiver receiver) { try { getContext().unregisterReceiver(receiver); } catch (IllegalArgumentException e) { // ignore } }
接下来的几个感觉大家应该都不会出太大的问题吧
Cursor
[code]try{}catch(){} finally{ cur.close(); cur=null; }
adapter (现在都用recycleview了。感觉写这个有点儿鸡肋)
[code]会造成内存溢出代码: public View getView (int position, View convertView, ViewGroup parent) { View view = new View(); XXX XXX return view; } 修正后的代码: public View getView (int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = new View(); XXX XXX } else { XXX XXX } }
Bitmap
不用的时候调用 recycle(),把他清理掉也可以用lrucache等方法,这就不做详细介绍。
转载请注明:/article/3657723.html
就这样吧,忙里偷闲写了个博客,总结一下,其实内存溢出的问题都是 不小心导致的,避免起来也比较容易。文章有点儿长 读完了 :辛苦了您內,初级文章大神勿喷
相关文章推荐
- android硬件加速(View.LAYER_TYPE_SOFTWARE)与GridView for ScrollView 显示问题
- Android_AnimationDrawable介绍及使用
- MTK android L使用汇顶TP如何使用B协议
- Android-->Fragment生命周期详解(上)
- android studio 用 Live Templates 自动生成switch、try、for、if
- Jenkins构建Android项目持续集成之findbugs的使用
- android 按钮两次点击事件区分
- androidstudio的gradle project sync failed解决
- Android应用:使用adb获得activity堆栈信息
- Android中多界面的退出
- Android拍照裁剪图片
- Android使用libgdx实现模拟方向键控制角色移动的方法
- android onKeydown
- VideoView 设置静音
- Android编程实现加载等待ProgressDialog的方法
- Android5.x之RecyclerView使用
- 8 个最优秀的 Android Studio 插件
- Android软键盘弹出时把布局顶上去的解决方法
- android自定义控件知识点
- (第一行代码笔记)调试Android程序