bitmap 图片优化

最近除了忙着项目开发上的事情,还有就是准备我的毕业论文,有一小段时间没写博客了,今晚难得想总结一下,刚好又有一点时间,于是凑合着来一篇,好了,唠叨话不多说,直接入正题。从事Android移动端的开发以来,想必是经常要与内存问题打交道的,说到Android开发中遇到的内存问题,像Bitmap这种吃内存的大户稍微处理不当就很容易造成OOM,当然,目前已经有很多知名的开源图片加载框架,例如:ImageLoader,Picasso等等,这些框架已经能够很好的解决了Bitmap造成的OOM问题,虽然这些框架能够节省很多开发者的宝贵时间,但是也会遇到一种情况,很多初学者只是会简单的去调用这些框架的提供的接口,被问到框架内部的一些实现原理,基本上都是脑中一片空白。从我的观点出发,我认为如果能够掌握一些框架原理,想必对我们进行应用调优的意义是非常重大的,今天,主要是是想谈谈,如果没有了图片加载框架,我们要怎么去处理Bitmap的内存问题呢?
谈到Bitmap处理的问题,我们可能要先来了解一些基础的知识,关于Bitmap在Android虚拟机中的内存分配,在Google的网站上给出了下面的一段话
技术分享
大致的意思也就是说,在Android3.0之前,Bitmap的内存分配分为两部分,一部分是分配在Dalvik的VM堆中,而像素数据的内存是分配在Native堆中,而到了Android3.0之后,Bitmap的内存则已经全部分配在VM堆上,这两种分配方式的区别在于,Native堆的内存不受Dalvik虚拟机的管理,我们想要释放Bitmap的内存,必须手动调用Recycle方法,而到了Android 3.0之后的平台,我们就可以将Bitmap的内存完全放心的交给虚拟机管理了,我们只需要保证Bitmap对象遵守虚拟机的GC Root Tracing的回收规则即可。OK,基础知识科普到此。接下来分几个要点来谈谈如何优化Bitmap内存问题。

1.Bitmap的引用计数方式(针对Android3.0之前平台的优化方案,先上Demo Code)

private int mCacheRefCount = 0;//缓存引用计数器
private int mDisplayRefCount = 0;//显示引用计数器
...
// 当前Bitmap是否被显示在UI界面上
public void setIsDisplayed(boolean isDisplayed) {
    synchronized (this) {
        if (isDisplayed) {
            mDisplayRefCount++;
            mHasBeenDisplayed = true;
        } else {
            mDisplayRefCount--;
        }
    }

    checkState();
}

//标记是否被缓存
public void setIsCached(boolean isCached) {
    synchronized (this) {
        if (isCached) {
            mCacheRefCount++;
        } else {
            mCacheRefCount--;
        }
    }

    checkState();
}

//用于检测Bitmap是否已经被回收
private synchronized void checkState() {
    if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
            && hasValidBitmap()) {
        getBitmap().recycle();
    }
}

private synchronized boolean hasValidBitmap() {
    Bitmap bitmap = getBitmap();
    return bitmap != null && !bitmap.isRecycled();
}

上面的实例代码,它使用了引用计数的方法(mDisplayRefCount 与 mCacheRefCount)来追踪一个bitmap目前是否有被显示或者是在缓存中. 当下面条件满足时回收bitmap:
mDisplayRefCount 与 mCacheRefCount 的引用计数均为 0.
bitmap不为null, 并且它还没有被回收.

2.使用缓存,LruCache和DiskLruCache的结合
关于LruCache和DiskLruCache,大家一定不会陌生(有疑问的朋友可以去API官网搜一下LruCache,而DiskLrucCache可以参考一下这篇不错的文章:DiskLruCache使用介绍),出于对性能和app的考虑,我们肯定是想着第一次从网络中加载到图片之后,能够将图片缓存在内存和sd卡中,这样,我们就不用频繁的去网络中加载图片,为了很好的控制内存问题,则会考虑使用LruCache作为Bitmap在内存中的存放容器,在sd卡则使用DiskLruCache来统一管理磁盘上的图片缓存。

3.SoftReference和inBitmap参数的结合
在第二点中提及到,可以采用LruCache作为存放Bitmap的容器,而在LruCache中有一个方法值得留意,那就是entryRemoved,按照文档给出的说法,在LruCache容器满了需要淘汰存放其中的对象腾出空间的时候会调用此方法(注意,这里只是对象被淘汰出LruCache容器,但并不意味着对象的内存会立即被Dalvik虚拟机回收掉),此时可以在此方法中将Bitmap使用SoftReference包裹起来,并用事先准备好的一个HashSet容器来存放这些即将被回收的Bitmap,有人会问,这样存放有什么意义?之所以会这样存放,还需要再提及到inBitmap参数(在Android3.0才开始有的,详情查阅API中的BitmapFactory.Options参数信息),这个参数主要是提供给我们进行复用内存中的Bitmap,如果设置了此参数,且满足以下条件的时候:

  • Bitmap一定要是可变的,即inmutable设置一定为ture;
  • Android4.4以下的平台,需要保证inBitmap和即将要得到decode的Bitmap的尺寸规格一致;
  • Android4.4及其以上的平台,只需要满足inBitmap的尺寸大于要decode得到的Bitmap的尺寸规格即可;

在满足以上条件的时候,系统对图片进行decoder的时候会检查内存中是否有可复用的Bitmap,避免我们频繁的去SD卡上加载图片而造成系统性能的下降,毕竟从直接从内存中复用要比在SD卡上进行IO操作的效率要提高几十倍。写了太多文字,下面接着给出几段Demo Code

Set<SoftReference<Bitmap>> mReusableBitmaps;
private LruCache<String, BitmapDrawable> mMemoryCache;

// 用来盛放被LruCache淘汰出列的Bitmap
if (Utils.hasHoneycomb()) {
    mReusableBitmaps =
            Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
}

mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {

    // 当LruCache淘汰对象的时候被调用,用于在内存中重用Bitmap,提高加载图片的性能
    @Override
    protected void entryRemoved(boolean evicted, String key,
            BitmapDrawable oldValue, BitmapDrawable newValue) {

        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {

            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
        } else {

            if (Utils.hasHoneycomb()) {

                mReusableBitmaps.add
                        (new SoftReference<Bitmap>(oldValue.getBitmap()));
            }
        }
    }
....
}

private static void addInBitmapOptions(BitmapFactory.Options options,
        ImageCache cache) {
        //将inMutable设置true,inBitmap生效的条件之一
    options.inMutable = true;

    if (cache != null) {
        // 尝试寻找可以内存中课复用的的Bitmap
        Bitmap inBitmap = cache.getBitmapFromReusableSet(options);

        if (inBitmap != null) {

            options.inBitmap = inBitmap;
        }
    }
}

// 获取当前可以满足复用条件的Bitmap,存在则返回该Bitmap,不存在则返回null
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
        Bitmap bitmap = null;

    if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
        synchronized (mReusableBitmaps) {
            final Iterator<SoftReference<Bitmap>> iterator
                    = mReusableBitmaps.iterator();
            Bitmap item;

            while (iterator.hasNext()) {
                item = iterator.next().get();

                if (null != item && item.isMutable()) {

                    if (canUseForInBitmap(item, options)) {
                        bitmap = item;
                        iterator.remove();
                        break;
                    }
                } else {

                    iterator.remove();
                }
            }
        }
    }
    return bitmap;
}

//判断是否满足使用inBitmap的条件
static boolean canUseForInBitmap(
        Bitmap candidate, BitmapFactory.Options targetOptions) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        // Android4.4开始,被复用的Bitmap尺寸规格大于等于需要的解码规格即可满足复用条件
        int width = targetOptions.outWidth / targetOptions.inSampleSize;
        int height = targetOptions.outHeight / targetOptions.inSampleSize;
        int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
        return byteCount <= candidate.getAllocationByteCount();
    }

    // Android4.4之前,必须满足被复用的Bitmap和请求的Bitmap尺寸规格一致才能被复用
    return candidate.getWidth() == targetOptions.outWidth
            && candidate.getHeight() == targetOptions.outHeight
            && targetOptions.inSampleSize == 1;
}

4.降低采样率,inSampleSize的计算
相信大家对inSampleSize是一定不会陌生的,所以此处不再做过多的介绍,关于降低采样率对inSampleSize的计算方法,我看到网上的算法有很多,下面的这段算法应该是最好的算法了,其中还考虑了那种宽高相差很悬殊的图片(例如:全景图)的处理。

public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }

            long totalPixels = width / inSampleSize * height / inSampleSize ;

            final long totalReqPixelsCap = reqWidth * reqHeight * 2;

            while (totalPixels > totalReqPixelsCap) {
                inSampleSize *= 2;
                totalPixels /= 2;
            }
        }
        return inSampleSize;

5.采用decodeFileDescriptor来编码图片(暂时不知道原理,欢迎高手指点迷津)
关于采用decodeFileDescriptor去处理图片可以节省内存这方面,我在写代码的时候进行过尝试,确实想比其他的decode方法要节省内存,查询了网上的解释,不是很清楚,自己看了一些源代码也弄不出个名堂,为什么使用这种方式就能够节省内存一些呢,如果有明白其中原理的高手,欢迎解答我的疑惑

到此,关于Bitmap处理的几个优化点已经分析完毕,就目前来说,可能大家在开发的过程习惯了使用框架来加载图片,所以不大在意图片内存处理的相关问题,如果你想知道一些优化Bitmap内存原理或者想自己做一个优秀的图片加载框架,希望本文能够为你提供一点点思路。如果读者觉得文章有错误,欢迎在下方评论中批评指正




Fields

 
public  Bitmap inBitmap If set, decode methods that take the Options object will attempt to reuse this bitmap when loading content.
public int inDensity The pixel density to use for the bitmap.
public boolean inDither If dither is true, the decoder will attempt to dither the decoded image.
public boolean inInputShareable This field works in conjuction with inPurgeable.
public boolean inJustDecodeBounds If set to true, the decoder will return null (no bitmap), but the out...
public boolean inMutable If set, decode methods will always return a mutable Bitmap instead of an immutable one.
public boolean inPreferQualityOverSpeed If inPreferQualityOverSpeed is set to true, the decoder will try to decode the reconstructed image to a higher quality even at the expense of the decoding speed.
public  Bitmap.Config inPreferredConfig If this is non-null, the decoder will try to decode into this internal configuration.
public boolean inPurgeable If this is set to true, then the resulting bitmap will allocate its pixels such that they can be purged if the system needs to reclaim memory.
public int inSampleSize If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
public boolean inScaled When this flag is set, if inDensity and inTargetDensity are not 0, the bitmap will be scaled to matchinTargetDensity when loaded, rather than relying on the graphics system scaling it each time it is drawn to a Canvas.
public int inScreenDensity The pixel density of the actual screen that is being used.
public int inTargetDensity The pixel density of the destination this bitmap will be drawn to.
public byte[] inTempStorage Temp storage to use for decoding.
public boolean mCancel Flag to indicate that cancel has been called on this object.
public int outHeight The resulting height of the bitmap, set independent of the state of inJustDecodeBounds.
public  String outMimeType If known, this string is set to the mimetype of the decoded image.
public int outWidth The resulting width of the bitmap, set independent of the state of inJustDecodeBounds.

上表是BitmapFactory.Options的字段,现在详细介绍各个字段的作用:

1.inBitmap 这个一般用在Android3.0或者更高版本中,用来管理Bitmap的内存用的。如果设置了这个字段,那么解码图片的时候通过Option类去重用inBitmap这个已经存在的Bitmap的内存去加载新的Bitmap。这意味着,内存的重用从而改进性能,避免重新分配内存。但是运用inBitmap时必须确保一下三点:

  • 重用的Bitmap必须和即将解码的Bitmap的尺寸相同,且是JPEG或者PNG格式。(PS,另外还需要设置options.inMutable = true
  • BitmapFactory.Options.inPreferredConfig字段设置无效,因为会被重用的Bitmap的configuration所覆盖。
  • 一定要使用解码方法返回的Bitmap,因为重用可能会失败(例如:尺寸不匹配)。
以下是代码示例:

首先需要声明来保持Bitmap(已经用过的Bitmap)

<span class="typ" style="color:#66066;">HashSet</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">SoftReference</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">Bitmap</span><span class="pun" style="color:#66660;">>></span><span class="pln" style="color:#000000;"> mReusableBitmaps</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#0088;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">LruCache</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">String</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">BitmapDrawable</span><span class="pun" style="color:#66660;">></span><span class="pln" style="color:#000000;"> mMemoryCache</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:#8800;">// 如果是3.0或者以上版本,注意这里必须使用软引用或者弱引用,从Android2.3(API 9)开始系统会更积极的回收被弱引用或者软引用所应用的///资源,在这里只是为了重用Bitmap内存来进一步提高性能,这里必须用软引用或者弱引用。至于图片的复用则用LruCache来实现(是强引用)。</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="typ" style="color:#66066;">Utils</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">hasHoneycomb</span><span class="pun" style="color:#66660;">())</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
    mReusableBitmaps </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">HashSet</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">SoftReference</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">Bitmap</span><span class="pun" style="color:#66660;">>>();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#66660;">}</span><pre name="code" class="prettyprint" style="font-size:13px; margin-top:0px; margin-bottom:1em; color:rgb(0,102,0); line-height:19px; padding:1em; overflow:auto; border:1px solid rgb(221,221,221); background-color:rgb(247,247,247)"><span class="pln" style="color:#000000;">mMemoryCache </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">LruCache</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">String</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">BitmapDrawable</span><span class="pun" style="color:#66660;">>(</span><span class="pln" style="color:#000000;">mCacheParams</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">memCacheSize</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">

    </span><span class="com" style="color:#8800;">// 通知释放不需要的缓存,这个方法在每次add新的缓存时调用</span><span class="pln" style="color:#000000;">
    </span><span class="lit" style="color:#06666;">@Override</span><span class="pln" style="color:#000000;">
    </span><span class="kwd" style="color:#0088;">protected</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">void</span><span class="pln" style="color:#000000;"> entryRemoved</span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">boolean</span><span class="pln" style="color:#000000;"> evicted</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">String</span><span class="pln" style="color:#000000;"> key</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;">
            </span><span class="typ" style="color:#66066;">BitmapDrawable</span><span class="pln" style="color:#000000;"> oldValue</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">BitmapDrawable</span><span class="pln" style="color:#000000;"> newValue</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
        </span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="typ" style="color:#66066;">RecyclingBitmapDrawable</span><span class="pun" style="color:#66660;">.</span><span class="kwd" style="color:#0088;">class</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">isInstance</span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">oldValue</span><span class="pun" style="color:#66660;">))</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
            </span><span class="com" style="color:#8800;">//自定义的BitmapDrawable,来降低它里面的引用计数</span><span class="pln" style="color:#000000;">
            </span><span class="pun" style="color:#66660;">((</span><span class="typ" style="color:#66066;">RecyclingBitmapDrawable</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> oldValue</span><span class="pun" style="color:#66660;">).</span><span class="pln" style="color:#000000;">setIsCached</span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">false</span><span class="pun" style="color:#66660;">);</span><span class="pln" style="color:#000000;">
        </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">else</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
            </span><span class="com" style="color:#8800;">// 标准BitmapDrawable.在这里注意,是把已经不用的Bitmap保存起来,这个方法是内存缓存的图片的大小超过了指定的大小,需要抛弃不需要的图片,就是说能够被垃圾回收的Bitmap,不是缓存的Bitmap(在LruCache中的Bitmap)</span><span class="pln" style="color:#000000;">
            </span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="typ" style="color:#66066;">Utils</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">hasHoneycomb</span><span class="pun" style="color:#66660;">())</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
                </span><span class="com" style="color:#8800;">// We're running on Honeycomb or later, so add the bitmap</span><span class="pln" style="color:#000000;">
                </span><span class="com" style="color:#8800;">// to a SoftReference set for possible use with inBitmap later.</span><span class="pln" style="color:#000000;">
                mReusableBitmaps</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">add
                        </span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">SoftReference</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">Bitmap</span><span class="pun" style="color:#66660;">>(</span><span class="pln" style="color:#000000;">oldValue</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">getBitmap</span><span class="pun" style="color:#66660;">()));</span><span class="pln" style="color:#000000;">
            </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
        </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
    </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#66660;">....</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#66660;">}</span>
 

复用已经存在的Bitmap的内存空间:

<span class="kwd" style="color:#0088;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">static</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">Bitmap</span><span class="pln" style="color:#000000;"> decodeSampledBitmapFromFile</span><span class="pun" style="color:#66660;">(</span><span class="typ" style="color:#66066;">String</span><span class="pln" style="color:#000000;"> filename</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;">
        </span><span class="kwd" style="color:#0088;">int</span><span class="pln" style="color:#000000;"> reqWidth</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln" style="color:#000000;"> reqHeight</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">ImageCache</span><span class="pln" style="color:#000000;"> cache</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">

    </span><span class="kwd" style="color:#0088;">final</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">BitmapFactory</span><span class="pun" style="color:#66660;">.</span><span class="typ" style="color:#66066;">Options</span><span class="pln" style="color:#000000;"> options </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">BitmapFactory</span><span class="pun" style="color:#66660;">.</span><span class="typ" style="color:#66066;">Options</span><span class="pun" style="color:#66660;">();</span><span class="pln" style="color:#000000;">
    </span><span class="pun" style="color:#66660;">options.inJustDecodeBounds = true;</span><span class="pln" style="color:#000000;">
    </span><span class="typ" style="color:#66066;">BitmapFactory</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">decodeFile</span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">filename</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> options</span><span class="pun" style="color:#66660;">);</span><span class="pln" style="color:#000000;">
    </span><span class="pun" style="color:#66660;">...</span><span class="pln" style="color:#000000;">

    </span><span class="com" style="color:#8800;">// 如果运行在Android3.0或者更高的版本,就重用intBitmap的内存空间</span><span class="pln" style="color:#000000;">
    </span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="typ" style="color:#66066;">Utils</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">hasHoneycomb</span><span class="pun" style="color:#66660;">())</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
        addInBitmapOptions</span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">options</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> cache</span><span class="pun" style="color:#66660;">);</span><span class="pln" style="color:#000000;">
    </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
    </span><span class="pun" style="color:#66660;">... //解码方法中的options的inBitmap的值有可能被设置过了,(就是重用Bitmap内存空间成功后会设置)</span><span class="pln" style="color:#000000;">
    </span><span class="kwd" style="color:#0088;">return</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">BitmapFactory</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">decodeFile</span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">filename</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> options</span><span class="pun" style="color:#66660;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#66660;">}</span>
addInBitmapOptions()方法对options进行了设置,给options的inBitmap设置了在HasSet中保存的Bitmap,这样就重用了已经存在的Bitmap的内存空间。一下是方法的代码:

<span class="kwd" style="color:#0088;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">void</span><span class="pln" style="color:#000000;"> addInBitmapOptions</span><span class="pun" style="color:#66660;">(</span><span class="typ" style="color:#66066;">BitmapFactory</span><span class="pun" style="color:#66660;">.</span><span class="typ" style="color:#66066;">Options</span><span class="pln" style="color:#000000;"> options</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;">
        </span><span class="typ" style="color:#66066;">ImageCache</span><span class="pln" style="color:#000000;"> cache</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
    </span><span class="com" style="color:#8800;">// inBitmap只有和inMutable一块使用才起作用,所以</span><span class="pln" style="color:#000000;">
    options</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">inMutable </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">true</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">

    </span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">cache </span><span class="pun" style="color:#66660;">!=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">null</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
        </span><span class="com" style="color:#8800;">//  尝试寻找可重用内存空间的Bitmap</span><span class="pln" style="color:#000000;">
        </span><span class="typ" style="color:#66066;">Bitmap</span><span class="pln" style="color:#000000;"> inBitmap </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> cache</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">getBitmapFromReusableSet</span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">options</span><span class="pun" style="color:#66660;">);</span><span class="pln" style="color:#000000;">

        </span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">inBitmap </span><span class="pun" style="color:#66660;">!=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">null</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
            </span><span class="com" style="color:#8800;">// 如果找到了,就给inBitmap设置值,这样就能用这个Bitmap的内存空间来生成新的Bitmap</span><span class="pln" style="color:#000000;">
            options</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">inBitmap </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> inBitmap</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">
        </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
    </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:#8800;">// 这个方法是在HasMap中寻找可重用内存空间的Bitmap</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#0088;">protected</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">Bitmap</span><span class="pln" style="color:#000000;"> getBitmapFromReusableSet</span><span class="pun" style="color:#66660;">(</span><span class="typ" style="color:#66066;">BitmapFactory</span><span class="pun" style="color:#66660;">.</span><span class="typ" style="color:#66066;">Options</span><span class="pln" style="color:#000000;"> options</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
        </span><span class="typ" style="color:#66066;">Bitmap</span><span class="pln" style="color:#000000;"> bitmap </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">null</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">

    </span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">mReusableBitmaps </span><span class="pun" style="color:#66660;">!=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">null</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">&&</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">!</span><span class="pln" style="color:#000000;">mReusableBitmaps</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">isEmpty</span><span class="pun" style="color:#66660;">())</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
        </span><span class="kwd" style="color:#0088;">final</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">Iterator</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">SoftReference</span><span class="pun" style="color:#66660;"><</span><span class="typ" style="color:#66066;">Bitmap</span><span class="pun" style="color:#66660;">>></span><span class="pln" style="color:#000000;"> iterator
                </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> mReusableBitmaps</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">iterator</span><span class="pun" style="color:#66660;">();</span><span class="pln" style="color:#000000;">
        </span><span class="typ" style="color:#66066;">Bitmap</span><span class="pln" style="color:#000000;"> item</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">
<span style="white-space:pre">	</span>//遍历HasMap中的Bitmap寻找尺寸和要生成的Bitmap尺寸相同的Bitmap
        </span><span class="kwd" style="color:#0088;">while</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">iterator</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">hasNext</span><span class="pun" style="color:#66660;">())</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
            item </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> iterator</span><span class="pun" style="color:#66660;">.</span><span class="kwd" style="color:#0088;">next</span><span class="pun" style="color:#66660;">().</span><span class="kwd" style="color:#0088;">get</span><span class="pun" style="color:#66660;">();</span><span class="pln" style="color:#000000;">

            </span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">null</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">!=</span><span class="pln" style="color:#000000;"> item </span><span class="pun" style="color:#66660;">&&</span><span class="pln" style="color:#000000;"> item</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">isMutable</span><span class="pun" style="color:#66660;">())</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
                </span><span class="com" style="color:#8800;">// 检测是否可被重用,就是检测其尺寸(长和宽)是否和要新生成的Bitmap的尺寸(长和宽)相同,</span><span class="pln" style="color:#000000;">
                </span><span class="kwd" style="color:#0088;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">canUseForInBitmap</span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">item</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> options</span><span class="pun" style="color:#66660;">))</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
                    bitmap </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> item</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">

                    </span><span class="com" style="color:#8800;">// 把这个能被重用内存空间的Bitmap从HashMap中删除,防止被两个以上重用导致的问题。</span><span class="pln" style="color:#000000;">
                    iterator</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">remove</span><span class="pun" style="color:#66660;">();</span><span class="pln" style="color:#000000;">
                    </span><span class="kwd" style="color:#0088;">break</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">
                </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
            </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">else</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
                </span><span class="com" style="color:#8800;">// Remove from the set if the reference has been cleared.</span><span class="pln" style="color:#000000;">
                iterator</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">remove</span><span class="pun" style="color:#66660;">();</span><span class="pln" style="color:#000000;">
            </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
        </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
    </span><span class="pun" style="color:#66660;">}</span><span class="pln" style="color:#000000;">
    </span><span class="kwd" style="color:#0088;">return</span><span class="pln" style="color:#000000;"> bitmap</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#66660;">}</span>
canUseForInBitmap()这个方法就是检测options中目标Bitmap的尺寸(是原始尺寸通过和inSampleSize计算能够得到要显示的Bitmap的尺寸)和HashMap中Bitmap的尺寸(由于是以前生成过的Bitmap所以一定是缩放过的)是否相同。一下是代码实现:

<span class="kwd" style="color:#0088;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#0088;">boolean</span><span class="pln" style="color:#000000;"> canUseForInBitmap</span><span class="pun" style="color:#66660;">(</span><span class="pln" style="color:#000000;">
        </span><span class="typ" style="color:#66066;">Bitmap</span><span class="pln" style="color:#000000;"> candidate</span><span class="pun" style="color:#66660;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#66066;">BitmapFactory</span><span class="pun" style="color:#66660;">.</span><span class="typ" style="color:#66066;">Options</span><span class="pln" style="color:#000000;"> targetOptions</span><span class="pun" style="color:#66660;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">{</span><span class="pln" style="color:#000000;">
    </span><span class="kwd" style="color:#0088;">int</span><span class="pln" style="color:#000000;"> width </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> targetOptions</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">outWidth </span><span class="pun" style="color:#66660;">/</span><span class="pln" style="color:#000000;"> targetOptions</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">inSampleSize</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">
    </span><span class="kwd" style="color:#0088;">int</span><span class="pln" style="color:#000000;"> height </span><span class="pun" style="color:#66660;">=</span><span class="pln" style="color:#000000;"> targetOptions</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">outHeight </span><span class="pun" style="color:#66660;">/</span><span class="pln" style="color:#000000;"> targetOptions</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">inSampleSize</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;">

    </span><span class="com" style="color:#8800;">// 如果尺寸(长和宽)相同,就返回true</span><span class="pln" style="color:#000000;">
    </span><span class="kwd" style="color:#0088;">return</span><span class="pln" style="color:#000000;"> candidate</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">getWidth</span><span class="pun" style="color:#66660;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">==</span><span class="pln" style="color:#000000;"> width </span><span class="pun" style="color:#66660;">&&</span><span class="pln" style="color:#000000;"> candidate</span><span class="pun" style="color:#66660;">.</span><span class="pln" style="color:#000000;">getHeight</span><span class="pun" style="color:#66660;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#66660;">==</span><span class="pln" style="color:#000000;"> height</span><span class="pun" style="color:#66660;">;</span><span class="pln" style="color:#000000;"></span>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值