解决Android平台中应用程序OOM异常的方法

在Android平台上面,应用程序OOM异常永远都是值得关注的问题。通常这一块也是程序这中的重点之一。这下我就如何解决OOM作一点简单的介绍。

首先,OOM就是内存溢出,即Out Of Memory。也就是说内存占有量超过了VM所分配的最大。

怎么解决OOM,通常OOM都发生在需要用到大量内存的情况下(创建或解析Bitmap,分配特大的数组等),在这样的一种情况下,就可能出现OOM,据我现在了解到,多数OOM都是因为Bitmap太大。所以,这里我就专门针对如何解决Bitmap的OOM。其实最核发的就是只加载可见范围内的Bitmap,试想这样一种情况,在GridView或ListView中,数据量有5000,每一屏只显示20个元素,那么不可见的,我们是不需要保存Bitmap在内在中的。所以我们就是只把那么可见的Bitmap保留在内存中,那些不可见的,就释放掉。当元素滑出来时,再去加载Bitmap。

这里我有两种方式,都可以避免OOM。

一、主动释放Bitmap的内存
这种方式我简单说一下,不太推荐,这也是我最开始使用的一种方法,但最后证明它不是最好的。(不推荐)

它的本质思路是:
  1、只加载可见区域的Bitmap

2、滑动时不加载

3、停止滑动(Idle)后,开始重新加载可见区域的图片

4、释放滑出可见区域的Bitmap的内在。

它比较复杂:
    1、我们需要监听GridView/ListView的滑动事件,这个很简单做到,AbsListView#setOnScrollListener(OnScrollListener l)

2、主动调用Bitmap#recycle()方法,它会导致一个问题,必须判断这个Bitmap是否被一个View(ImageView等)所引用,如果被引用,我们不能简单地调用recycle()方法,这样会导致异常,说是View使用了一个已经被回收的Bitmap。

3,我们必须设计自己的线程来控制开始/暂停等,因为GridView/ListView的滑动状态可能不断地变化,也就是说滑动->停止->滑动,这种状态可能不断变化,这样就会导致我们的线程中的run()方法里面的逻辑比较复杂,一旦复杂,问题就可能就得更多。

基于以上几点,这种方式不是最好的,所以不推荐。

二、设计Cache
    这种方式,我觉得是比较好的一种,它首先利用了cache,我认为cache是一个很重要的东西,把Bitmap的内存单独放在一个地方来管理,这个地方就是cache,它的容量是一定的,我们可能会不断的向这个cache中添加元素,也可能不断的移除元素。

为了更好的说明这种方式,先要介绍一下LruCache。

LruCache
    1、这其实就是一个LinkedHashMap,任意时刻,当一个值被访问时,它就会被移动到队列的开始位置,所以这也是为什么要用LinkedHashMap的原因,因为要频繁的做移动操作,为了提高性能,所以要用LinkedHashMap。当cache满了时,此时再向cache里面添加一个值,那么,在队列最后的值就会从队列里面移除,这个值就有可能被GC回收掉。

2、如果我们想主动释放内存,也是可以的,我们可以重写entryRemoved(Boolean, K, V, V)方法。

3、这个类是线程安全的,在多线程下面使用这个类,没不会存在问题。

synchronized (cache) {
   if (cache.get(key) == null) {
     cache.put(key, value);
  }}

4、LruCache的APILevel是12,也就是说,我们在SDK 2.3.x以下是无法使用的,但是没关系,LruCache的源码不算复杂,我们可以直接把它拷贝到自己的工程目录就可以了。
 AsyncTask<>
    这个类也是一个很重要也很常用的类。它封装了Thread和Handler,我们使用就更加方便,不用关注Handler,我们知道,在后台线程中是不能更新UI,而很多情况下,我们在后台线程做完一件事情后,一般都会更新UI,一般的做法是向关联到UI线程的Handler发送一个message,在Handler里面去处理这个message,从而更新UI。用了AsyncTask之后,我们就不用关注Handler了。这个类有几个重要的方法:

1)、onPreExecute(): 在UI线程里面调用,它在这个task执行后会立即调用。我们在这个方法里面通常是用于建立一个任务,比如显示一个等待对话框来通知用户。

2)、doInBackground(Params...):这个方法从名字就可以看出,它是运行在后台线程的,在这个方法里面,去做耗时的事情,比如下载访问网络,操作文件等。这这个方法里面,我们可以调用publishProgress(Progress...)来调用当前任务的进度,调用了这个方法后,对应的onProgressUpdate(Progress...)方法会被调用,这个方法是运行在UI线程的。

3)、onProgressUpdate(Progress...):运行在UI线程,在调用publishProgress()方法之后。这个方法用来在UI上显示任何形式的进度,比如你可以显示一个等待对话框,也可以显示一个文本形式的log,还可以显示toast对话框。

4)、onPostExecute(Result):当task结束后调用,它运行在UI线程。

5)、取消一个task,我们可以在任何时候调用cancel(Boolean)来取消一个任务,当调用了cancel()方法后,onCancelled(Object)方法就会被调用,onPostExecute(Object)方法不会被调用,在doInBackground(Object[])方法中,我们可以用isCancelled()方法来检查任务是否取消。

6)、几点规则

AsyncTask实例必须在UI线程中创建  
execute(Params...)方法必须在UI线程中调用。
不用手动调用onPreExecute(), onPostExecute(), doInBackground(), onProgressUpdate()方法。
一个任务只能被执行一次。 
总的思路
    1、始终从cache中去取Bitmap,如果取到Bitmap,就直接把这个Bitmap设置到ImageView上面。

2、如果缓存中不存在,那么启动一个task去加载(可能从文件来,也可能从网络)。

3、每一个ImageView上面都可能绑定一个task,所以,这个ImageView必须提供一个方法能得到与之相关联的task,为什么要这样做?因为在给一个ImageView绑定task之前,必须要把原先的task取消。

以上就是解决应用程序OOM异常的方法,希望对大家的学习有所帮助。

(0)

相关推荐

  • Android开发 -- setTag的妙用和The key must be an application-specific resource id 异常

    tag是view的一个属性,也可以说是view用于存放参数的一个map,对于提高性能和参数传递都有妙用,比如提高listview的性能: 用于缓存item的view 复制代码 代码如下: public View getView(final int position, View convertView, ViewGroup parent) {      ItemViewHolder holder; if (convertView == null) {          holder = new I

  • android 捕获系统异常并上传日志具体实现

    在做项目时,经常会把错误利用异常抛出去,这样在开发时就可以通过手机抛出的异常排查错误.但是当程序开发完毕,版本稳定,需要上线时,为了避免抛出异常影响用户感受,可以用UncaughtExceptionHandler捕获全局异常,对异常做出处理.比如我们可以获取到抛出异常的时间.手机的硬件信息.错误的堆栈信息,然后将获取到的所有的信息发送到服务器中,也可以发送到指定的邮件中,以便及时修改bug. 示例: 自定义异常类实现UncaughtExceptionHandler接口,当某个页面出现异常就会调用

  • Android 后台发送邮件示例 (收集应用异常信息+Demo代码)

    上一次说了如何收集我们已经发布的应用程序的错误信息,方便我们调试完善程序.上次说的收集方法主要是把收集的信息通过Http的post请求把相关的异常信息变成请求参数发送到服务器.这个对做过web开发的人来说,服务端处理是很简单.不过对很多没做个web的人来说却是麻烦事.今天介绍个更简单的方法,我们把异常信息收集后,通过后台发送邮件方法,把相关异常信息发送到我们指定的邮箱里面. 这篇文章是实用性文章,不会涉及太多理论分析.主要是让大家看了以后知道怎么在自己的应用里面添加这个功能. 1.第三方库这次发

  • 详解Android中处理崩溃异常

    大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了崩溃现象,开发者应该及时获取在该设备上导致崩溃的信息,这对于下一个版本的bug修复帮助极大,所以今天就来介绍一下如何在程序崩溃的情况下收集相关的设备参数信息和具体的异常信息,并发送这些信息到服务器供开发者分析和调试程序. 我们先建立一个crash项目,项目结构如图: 在MainActivity.ja

  • Android异常处理最佳实践

    一个好的app 异常处理机制 我认为应该至少包含以下几个功能: 1.能把错误信息上传到服务器  让开发者可以持续改进app 2.错误信息至少应该包含 是否在主进程 是否在主线程 等可以帮助程序员定位的信息 3.最好包含手机硬件及软件信息. 4.主进程引发的异常 最好交由系统自己处理 也就是让用户可以感知到 那种(当然你也可以自己定义一套更有意思的感知系统对话框等,具体可参考各种有意思的404界面) 5.子进程引发的异常最好别让用户感知到.比如push之类的 这种 和用户感知弱关联的这种.最好发生

  • 详解Android全局异常的捕获处理

    在Android开发中在所难免的会出现程序crash,俗称崩溃.用户的随意性访问出现测试时未知的Bug导致我们的程序crash,此时我们是无法直接获取的错误log的,也就无法修复Bug.这就会极大的影响用户体验,此时我们需要注册一个功能来捕获全局的异常信息,当程序出现crash信息,我们把错误log记录下来,上传到服务器,以便于我们能及时修复bug.实现这个功能我们需要依赖于UncaughtExceptionHandler这个类,UncaughtExceptionHandler是一个接口,在Th

  • Android崩溃异常捕获方法

    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面.而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里.但平时使用的时候给你闹崩溃,那你就欲哭无泪了. 那么今天主要讲一下如何去捕捉系统出现的Unchecked异常.何为Unchecked异常呢,换句话说就是指非受检异常,它不能用try-catch来显示捕捉. 我们先从Exception讲起.Exception分为两类:一种是CheckedException,一种是UncheckedException

  • Android开发中优秀的app 异常处理机制

    一个好的app 异常处理机制 我认为应该至少包含以下几个功能: 1.能把错误信息上传到服务器  让开发者可以持续改进app 2.错误信息至少应该包含 是否在主进程 是否在主线程 等可以帮助程序员定位的信息 3.最好包含手机硬件及软件信息. 4.主进程引发的异常 最好交由系统自己处理 也就是让用户可以感知到 那种(当然你也可以自己定义一套更有意思的感知系统对话框等,具体可参考各种有意思的404界面) 5.子进程引发的异常最好别让用户感知到.比如push之类的 这种 和用户感知弱关联的这种.最好发生

  • Android开发使用UncaughtExceptionHandler捕获全局异常

    在集成了统计SDK(友盟统计,百度统计等)之后,有一个非常有利于测试的功能:错误分析!此功能能够将程序在运行中碰到的崩溃(runtimeException)问题反馈到服务器,帮助开发者改善产品,多适配机器. 然而在公司Android开发中不集成这些SDK,那应该怎么实现这样的功能呢?下面让我们来看下如何使用UncaughtExceptionHandler来捕获异常. 首先实现创建一个类,实现UncaughtExceptionHandler接口.代码如下: 复制代码 代码如下: public cl

  • Android中捕获全局异常实现代码

    1.实现UncaughtExceptionHandler,在方法uncaughtException中处理没有捕获的异常. public class GlobalException implements UncaughtExceptionHandler { private final static GlobalException myCrashHandler = new GlobalException(); private GlobalException() { } public static s

  • android教程之hockeyapp捕获异常示例

    复制代码 代码如下: package com.example.testhockeyapp;import net.hockeyapp.android.CrashManager;import net.hockeyapp.android.CrashManagerListener;import net.hockeyapp.android.UpdateManager;import android.os.Bundle;import android.app.Activity;import android.vi

随机推荐