`
kongweile
  • 浏览: 517404 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Android中解决图像解码导致的OOM问题

 
阅读更多

http://7dot9.com/2011/08/android%E4%B8%AD%E8%A7%A3%E5%86%B3%E5%9B%BE%E5%83%8F%E8%A7%A3%E7%A0%81%E5%AF%BC%E8%87%B4%E7%9A%84oom%E9%97%AE%E9%A2%98/

在上一篇博文Android Bitmap内存限制中我们详细的了解并分析了Android为什么会在Decode Bitmap的时候出现OOM错误,简单的讲就是Android在解码图片的时候使用了本地代码来完成解码的操作,但是使用的内存是堆里面的内存,而堆内存的大小是收VM实例可用内存大小的限制的,所以当应用程序可用内存已经无法再满足解码的需要时,Android将抛出OOM错误。

这里讲一个题外话,也就是为何Android要限制每个应用程序的可用内存大小呢?其实这个问题可能有多方面的解答,目前我自己考虑到的有两点:

  1. 使得内存的使用更为合理,限制每个应用的可用内存上限,可以防止某些应用程序恶意或者无意使用过多的内存,而导致其他应用无法正常运行,我们众所周知的Android是有多进程的,如果一个进程(也就是一个应用)耗费过多的内存,其他的应用还搞毛呢?当然在这里其实是有一个例外,那就是如果你的应用使用了很多本地代码,在本地代码中创建对象解码图像是不会被计算到的,这是因为你使用本地方法创建的对象或者解码的图像使用的是本地堆的内存,跟系统是平级的,而我们通过Framework调用BitmapFactory.decodeFile()方法解码时,系统虽然也是调用本地代码来进行解码的,但是Android Framework在实现的时候,刻意地将这部分解码使用的内存从堆里面分配了而不是从本地堆里分配的内存,所以才会出现OOM,当然并不是说从本地堆里分配就不会出现OOM,本地堆分配内存超过系统可用内存限制的话,通常都是直接崩溃,什么错误可能都看不到,也许会有一些崩溃的错误字节码之类的。
  2. 省电的考虑,呃…,原因我好像也不能很明白地说出来。

回到正题来,我们在应用的设计和开发中可能会经常碰到需要在一个界面上显示数十张图片乃至上百张,当然限于手机屏幕的大小我们通常在设计中会使用类似于列表或者网格的控件来展示,也就是说通常一次需要显示出来图片数还是一个相对确定的数字,通常也不会太大。如果数目比较大的画,通常显示的控件自身尺寸就会比较小,这个时候可以采用缩略图策略。下面我们来看看如果避免出现OOM的错误,这个解决方案参考了Android示范程序XML Adapters中的ImageDownloader.java中的实现,主要是使用了一个二级缓存类似的机制,就是有一个数据结构中直接持有解码成功的Bitmap对象引用,同时使用一个二级缓存数据结构持有解码成功的Bitmap对象的SoftReference对象,由于SoftReference对象的特殊性,系统会在需要内存的时候首先将SoftReference对象持有的对象释放掉,也就是说当VM发现可用内存比较少了需要触发GC的时候,就会优先将二级缓存中的Bitmap回收,而保有一级缓存中的Bitmap对象用于显示。

其实这个解决方案最为关键的一点是使用了一个比较合适的数据结构,那就是LinkedHashMap类型来进行一级缓存Bitmap的容器,由于LinkedHashMap的特殊性,我们可以控制其内部存储对象的个数并且将不再使用的对象从容器中移除,这就给二级缓存提供了可能性,我们可以在一级缓存中一直保存最近被访问到的Bitmap对象,而已经被访问过的图片在LinkedHashMap的容量超过我们预设值时将会把容器中存在时间最长的对象移除,这个时候我们可以将被移除出LinkedHashMap中的对象存放至二级缓存容器中,而二级缓存中对象的管理就交给系统来做了,当系统需要GC时就会首先回收二级缓存容器中的Bitmap对象了。在获取对象的时候先从一级缓存容器中查找,如果有对应对象并可用直接返回,如果没有的话从二级缓存中查找对应的SoftReference对象,判断SoftReference对象持有的Bitmap是否可用,可用直接返回,否则返回空。

主要的代码段如下:

private static final int HARD_CACHE_CAPACITY = 16;

// Hard cache, with a fixed maximum capacity and a life duration 
private static final HashMap<String, Bitmap> sHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) { 
    private static final long serialVersionUID = -57738079457331894L;

    @Override 
    protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) { 
        if (size() > HARD_CACHE_CAPACITY) { 
            // Entries push-out of hard reference cache are transferred to soft reference cache 
            sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue())); 
            return true; 
        } else 
            return false; 
    } 
};

// Soft cache for bitmap kicked out of hard cache 
private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);

/** 
* @param id 
*            The ID of the image that will be retrieved from the cache. 
* @return The cached bitmap or null if it was not found. 
*/ 
public Bitmap getBitmap(String id) { 
    // First try the hard reference cache 
    synchronized (sHardBitmapCache) { 
        final Bitmap bitmap = sHardBitmapCache.get(id); 
        if (bitmap != null) { 
            // Bitmap found in hard cache 
            // Move element to first position, so that it is removed last 
            sHardBitmapCache.remove(id); 
            sHardBitmapCache.put(id, bitmap); 
            return bitmap; 
        } 
    }

    // Then try the soft reference cache 
    SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(id); 
    if (bitmapReference != null) { 
        final Bitmap bitmap = bitmapReference.get(); 
        if (bitmap != null) { 
            // Bitmap found in soft cache 
            return bitmap; 
        } else { 
            // Soft reference has been Garbage Collected 
            sSoftBitmapCache.remove(id); 
        } 
    }

    return null; 
}

public void putBitmap(String id, Bitmap bitmap) { 
    synchronized (sHardBitmapCache) { 
        if (sHardBitmapCache != null) { 
            sHardBitmapCache.put(id, bitmap); 
        } 
    } 
}

上面这段代码中使用了id来标识一个Bitmap对象,这个可能大家在实际的应用中可以选择不同的方式来索引Bitmap对象,图像的解码在这里就不做赘述了。这里主要讨论的就是如何管理Bitmap对象,使得在实际应用中不要轻易出现OOM错误,其实在这个解决方案中,HARD_CACHE_CAPACITY的值就是一个经验值,而且这个跟每个应用中需要解码的图片的实际大小直接相关,如果图片偏大的话可能这个值还得调小,如果图片本身比较小的话可以适当的调大一些。本解决方案主要讨论的是一种双缓存结合使用SoftReference的机制,通过使用二级缓存和系统对SoftReference对象的回收特性,让系统自动回收不再敏感的图片Bitmap对象,而保有一级缓存也就是敏感的图片Bitmap对象。

分享到:
评论

相关推荐

    android之Gif处理(解决GIF显示容易OOM问题,包括代码和说明),非常详细的介绍

    本篇文章将深入探讨如何在Android中有效地处理GIF,以避免OOM问题。 一、GIF的内存占用问题 GIF是一种基于帧的图像格式,每一帧都是一个完整的图像。当在Android中加载GIF时,如果不做特殊处理,所有帧都会被加载到...

    Android解决图片OOM问题

    在Android开发中,Out Of Memory (OOM) 是一个常见的问题,尤其在处理图像资源时更为突出。本示例主要探讨如何解决Android应用中的图片OOM问题,以确保应用的稳定性和性能。 首先,我们需要理解Android系统对内存...

    大量加大drawable下图片,导致OOM,使用二次裁剪

    这个问题在标题“大量加大drawable下图片,导致OOM,使用二次裁剪”中被提及,暗示了解决这一问题的一种策略——二次裁剪和图片压缩。 首先,我们要理解什么是OOM。在Java或Android环境中,每个应用都有其固定的...

    安卓gif加载解决oom

    首先,理解Android内存管理机制是解决问题的关键。Android系统为每个应用分配一定的内存预算,当应用超过这个预算时,就会抛出OOM异常。对于大内存消耗的资源,如高分辨率的图片或动态图,不合理的加载和缓存策略...

    ANDROIDBITMAP内存限制OOM,OUTOFMEMORY.pdf

    文档标题和描述中提到的“ANDROIDBITMAP内存限制OOM,OUTOFMEMORY”指的就是在处理位图(BITMAP)时超出了虚拟机(VM)的内存预算,导致系统抛出OutOfMemoryError异常。 根据给出的内容部分,我们可以推断出以下知识...

    处理android bitmap oom

    在Android开发中,Bitmap对象是用于处理图像的主要类,但如果不妥善管理,它可能会引发“Out Of Memory”(OOM)错误。这是因为Android设备的内存有限,尤其是对于大型图像,加载到内存中会消耗大量资源。当应用无法...

    处理android bitmap oom 2.0版本

    "处理android bitmap oom 2.0版本"是一个针对这个问题的解决方案更新,它旨在修复前一版本中回收Bitmap对象可能导致的问题。 1. **理解Bitmap OOM**: - OOM错误通常发生在Android系统无法为应用分配更多内存时。 ...

    bitmap OOM的解决方案

    在Android开发中,Bitmap对象是用于处理图像的重要类,但不当使用可能会引发“Out Of Memory”(OOM)错误。Bitmap OOM通常是由于加载过大或过多的图片资源导致内存溢出。以下是对这个问题的深入探讨和解决方案。 ...

    加载图片oom的解决

    在Android开发中,由于系统对每个应用程序分配的内存有限,加载大尺寸的图片可能会导致“Out Of Memory”(OOM)异常,从而影响应用的稳定性和性能。为了解决这个问题,我们需要掌握一些有效的策略来优化图片加载,...

    解决Android解析图片的OOM问题的方法!!!

    解决Android解析图片的OOM问题主要涉及到对Bitmap对象的合理管理和高效加载策略。下面将详细介绍如何避免和解决这一问题。 首先,我们需要理解BitmapFactory类在加载图片时的角色。BitmapFactory提供了一系列的...

    android_picture.zip_Android图像处理_android

    因此,开发者通常会使用像Glide、Picasso或 Fresco这样的库来高效地加载和显示图片,它们能自动处理内存缓存和磁盘缓存,避免OOM问题。 3. **图像滤镜**:Android支持对图像应用各种滤镜效果,如灰度、对比度调整、...

    Android中对图像进行Base64编码

    在Android开发中,有时我们需要将图像转换为字符串形式以便在网络传输或者存储到数据库中,这时就会用到Base64编码。Base64是一种用于将二进制数据转换为可打印字符的编码方式,广泛应用于电子邮件、JSON数据以及...

    EditText中插入图片并解决图片OOM

    这涉及到Android图像处理和内存管理的重要问题——如何避免图片导致的内存溢出(Out Of Memory,简称OOM)。 首先,我们需要理解Android的Bitmap对象是如何处理图像的。Bitmap占用大量内存,因为它存储了每个像素的...

    gif加载动画不oom

    GIF格式的图片包含了连续的帧,当使用默认的Bitmap解码方式时,会一次性加载所有帧到内存中,这样对于大图或者多图场景,内存消耗急剧增加,进而可能导致OOM。 针对这一问题,我们可以采取以下策略来优化GIF加载: ...

    Android应用源码之加载本地图片,绝对不会出现OOM.zip

    在Android应用开发中,加载本地图片是一个常见的任务,但如果不恰当处理,很容易导致内存溢出(Out Of Memory,简称OOM)问题。这个压缩包“Android应用源码之加载本地图片,绝对不会出现OOM.zip”提供了避免Android...

    ANDROIDBITMAP内存限制OOM,OUTOFMEMORY[文].pdf

    Bitmap对象是Android系统中用于处理图像数据的重要类,但由于其消耗大量的内存,不当使用可能导致应用程序崩溃。本文将深入探讨Android Bitmap内存限制以及如何避免OOM错误。 首先,我们需要理解引发上述错误的原因...

    Android高级应用源码-图片下载以及内存处理防OOM.zip

    这个“Android高级应用源码-图片下载以及内存处理防OOM.zip”压缩包提供了一些解决这些问题的高级策略和源代码示例。 首先,我们来探讨一下图片下载。在Android应用中,网络图片的下载通常通过异步任务完成,以避免...

    安卓Android源码——加载本地图片,绝对不会出现OOM.zip

    这个压缩包“安卓Android源码——加载本地图片,绝对不会出现OOM.zip”显然是提供了避免OOM问题的一种解决方案。下面我们将深入探讨Android中如何有效地加载本地图片,并避免内存溢出。 首先,我们需要理解为什么...

Global site tag (gtag.js) - Google Analytics