android中的内存溢出预计大多数人在写代码的时候都出现过,事实上突然认为工作一年和工作三年的差别是什么呢。事实上干的工作或许都一样,产品汪看到的结果也都一样,那差别就是速度和质量了。
写在前面的一点儿想法:工作做完了事实上不会的还有非常多,每天都有莫名的危机感,从真正写代码的这一年多总认为自己的学习速度比别人的慢非常多
内存泄漏是什么鬼?
当某些对象不再被程序所使用。可是这些对象仍然被某些对象所引用着。进而导致垃圾收集器不能及时释放它们。
(无效的对象没有及时回收。导致内存不够用。致使程序
出错)
来个图片了解一下
知道为什么导致内存泄露那就非常好办了。都是跟对象有关系(就是new出来的 不要想着他会跟你结婚)
主要有以下几方面吧:平时注意一下 全然能够杜绝的
- Context
- 内部类(handler等)
- Cursor
- Adapter
- Bitmap
Context的溢出
来个图让大家分分钟理解一下:
看到这个图在稍加思索会不会认为我们的工具类 貌似好多都持有了activity,并且工具类还是static类型。
在细琢磨一下呢。是不是activity的上下文都能够被application替代呢?
经验之谈:dialog ,fragment。inflate和启动activity 的上下文都是activity的。其它的都都能够被application替代。比方数据库的 服务的 广播的。都不要再用activity了吧。
当然也要酌情处理。
- 举个栗子(太多了根本举只是来)
1.获取系统的服务
getSystemService(Context.SENSOR_SERVICE);
改为 getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
2.弄个数据库
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的情况
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。那么久做个推断吧
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销毁的事后 直接让线程池关闭即可啦
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()?
这个错误我们如今公司的代码还有大把的了。这么解决,一个大神的方法
简单粗暴
private void unregisterReceiverSafe(BroadcastReceiver receiver) {
try {
getContext().unregisterReceiver(receiver);
} catch (IllegalArgumentException e) {
// ignore
}
}
接下来的几个感觉大家应该都不会出太大的问题吧
Cursor
try{}catch(){}
finally{
cur.close();
cur=null;
}
adapter (如今都用recycleview了。感觉写这个有点儿鸡肋)
会造成内存溢出代码:
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等方法,这就不做具体介绍。
转载请注明:http://blog.csdn.net/wanghao200906/article/details/50426881
就这样吧。忙里偷闲写了个博客,总结一下,事实上内存溢出的问题都是 不小心导致的,避免起来也比較easy。
文章有点儿长 读完了 :辛苦了您內,0基础文章大神勿喷