最常见的Android内存优化方式及防止泄漏造成OOM总结篇( 四 )


6、尽量避免使用 static 成员变量
如果成员变量被声明为 static,那我们都知道其生命周期将与整个app进程生命周期一样 。
这会导致一系列问题,如果你的app进程设计上是长驻内存的,那即使app切到后台,这部分内存也不会被释放 。按照现在手机app内存管理机制,占内存较大的后台进程将优先回收,如果此app做过进程互保保活,那会造成app在后台频繁重启 。当手机安装了你参与开发的app以后一夜时间手机被消耗空了电量、流量,你的app不得不被用户卸载或者静默 。
修复的方法:

  • 不要在类初始时初始化静态成员 。可以考虑lazy初始化 。
7、AsyncTask对象造成的泄漏
AsyncTask确实需要额外注意一下 。它的泄露原理和前面Handler,Thread泄露的原理差不多,它的生命周期和Activity不一定一致 。
解决方案:在activity退出的时候,终止AsyncTask中的后台任务 。
但是,问题是如何终止?
AsyncTask提供了对应的API:public final boolean cancel (boolean mayInterruptIfRunning) 。
它的说明有这么一句话:
// Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason.// If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.cancel是不一定成功的,如果正在运行,它可能会中断后台任务 。怎么感觉这话说的这么不靠谱呢?
是的,就是不靠谱 。
那么,怎么才能靠谱点呢?我们看看官方的示例:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called // 注意下面这行,如果检测到cancel,则及时退出 if (isCancelled()) break; } return totalSize; }protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); }protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }官方的例子是很好的,在后台循环中时刻监听cancel状态,防止没有及时退出 。
为了提醒大家,google特意在AsyncTask的说明中撂下了一大段英文:
// AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.可怜我神州大陆幅员辽阔,地大物博,什么都不缺,就是缺对英语阅读的敏感 。
AsyncTask适用于短耗时操作,最多几秒钟 。如果你想长时间耗时操作,请使用其他java.util.concurrent包下的API,比如Executor, ThreadPoolExecutor 和 FutureTask.
学好英语,避免踩坑!
8、 BroadcastReceiver对象
种种原因没有调用到unregister()方法 。
解决方法很简单,就是确保调用到unregister()方法 。
顺带说一下,我在工作中碰到一种相反的情况,receiver对象没有registerReceiver()成功(没有调用到),于是unregister的时候提示出错:
// java.lang.IllegalArgumentException: Receiver not registered ...解决方案:
方案一:在registerReceiver()后设置一个FLAG,根据FLAG判断是否unregister() 。网上搜到的文章几乎都这么写,我以前碰到这种bug,也是一直都这么解 。但是不可否认,这种代码看上去确实有点丑陋 。
方案二:我后来无意中听到某大牛提醒,在Android源码中看到一种更通用的写法:
// just sample, 可以写入工具类// 第一眼我看到这段代码,靠,太粗暴了,但是回头一想,要的就是这么简单粗暴,不要把一些简单的东西搞的那么复杂 。private void unregisterReceiverSafe(BroadcastReceiver receiver) { try { getContext().unregisterReceiver(receiver); } catch (IllegalArgumentException e) { // ignore }}9、BitMap对象造成的泄漏
Bitmap 对象不用的时候最好调用一下recycle 方法再赋值null,清空资源的直接或间接引用,但是有人要问,android源码里面好多地方也没有调用啊?
是的,我这里说的是最好,如果不调用的话,只能依赖于Java GC 执行的时候,调用Bitmap 的 finalize方法,


推荐阅读