Android官网培训:管理位图内存

在《缓存位图》一课中,我们用缓存的方法提高了位图的重用,除了cache缓存的应用以外,你还可以做其他几个事情来帮助虚拟机进行内存回收和位图重用。根据你的app的目标android版本的不同,我们所推荐的策略也不相同。BitmapFun样例说明了如何在跨越不同版本android的情况下设计app,使app高效的工作。

在Android2.2(API level 8)以及更低版本,当内存回收发生时,你的app的线程会被停止,导致一定延迟,降低执行效率。android2.3添加了并发的垃圾回收,当位图不在被引用的时候,内存就被回收重用。
在Android2.3.3(API level 10)以及更低版本,位图的pixel data被存储在native memory中,而位图本身是存储在Dalvik heap中。native memory中的pixel data不会以可预知的方式进行释放,因此可能会让一个app超出内存限制而崩溃。Android3.0(API level 11)中pixel data和其关联的位图均存放在Dalvik heap中。
下面章节描述了在不同的Android版本下,如何优化位图内存的管理。

  • Android2.3.3及更低版本内存管理
在Android2.3.3(API level 10)及更低版本,推荐用recycle()。如果在你的app中展示大量的位图数据,很可能导致OutOfMemoryError错误。recycle()方法允许app尽可能回收内存。
注:仅当你确定位图数据不会再被使用时,你才可以调用recycle()。如果你先调用了recycle()然后试图重绘这个位图,你会收到错误”Canvas:trying to use a recycled bitmap"。
以下代码片段给出了一个调用recycle()的例子。这个例子用引用计数(mDisplayRefCount和mCacheRefCount)来跟踪一个位图是正在被显示还是在缓存之中。当遇到以下情况这段代码会recycle位图。
1.mDisplayRefCount和mCacheRefCount都是0
2.bitmap不是null,并且它还未被recycle。

private
  int  mCacheRefCount  =  0 ;
private  int  mDisplayRefCount  =  0 ;
...
// Notify the drawable that the displayed state has changed.
// Keep a count to determine when the drawable is no longer displayed.
public  void  setIsDisplayed ( boolean  isDisplayed )  {
synchronized  ( this )  {
if  ( isDisplayed )  {
mDisplayRefCount
++;
mHasBeenDisplayed
  =  true ;
}  else  {
mDisplayRefCount
--;
}
}
// Check to see if recycle() can be called.
checkState
();
}

// Notify the drawable that the cache state has changed.
// Keep a count to determine when the drawable is no longer being cached.
public  void  setIsCached ( boolean  isCached )  {
synchronized  ( this )  {
if  ( isCached )  {
mCacheRefCount
++;
}  else  {
mCacheRefCount
--;
}
}
// Check to see if recycle() can be called.
checkState
();
}

private  synchronized  void  checkState ()  {
// If the drawable cache and display ref counts = 0, and this drawable
// has been displayed, then recycle.
if  ( mCacheRefCount  <=  0  &&  mDisplayRefCount  <=  0  &&  mHasBeenDisplayed
&&  hasValidBitmap ())  {
getBitmap
(). recycle ();
}
}

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



  • 在Android3.0及以上版本管理内存
Android3.0(API level 11)引入了BitmapFactory.Options.inBitmap。如果设置了这个选项,解码方法会在加载内容时尝试重用已经存在的bitmap。这意味着bitmap的内存被重用,带来的结果是性能的提升,并且减少了内存分配和内存释放。inBitmap选项有几点注意事项:
1.被重用的bitmap必须和试图加载的源内容的尺寸相同(为了保证内存大小一样),并且必须是JPEG或者PNG格式(无论是以resoucre形式还是stream形式)。
2.被重用的bitmap的configuration会覆盖对inPreferredConfig的设定。
3.你应该坚持使用解码方法返回的bitmap,因为bitmap的重用不保证一定有效(例如,尺寸不匹配)。

为不久将来的使用而保存一个位图
以下的代码片段阐释了如何为不久将来的使用而存储一个bitmap。当一个app是运行在Andorid3.0及更高的版本上,当一个位图从LruCache被剔除出来时,对该位图的一个soft引用会被置于一个HashSet中,以备将来inBitmap的重用。
HashSet<SoftReference<Bitmap>> mReusableBitmaps;
private LruCache<String, BitmapDrawable> mMemoryCache;

// 如果要运行在Honeycomb或更新的版本上,创建bitmap的引用的HashSet
if (Utils.hasHoneycomb()) {
    mReusableBitmaps = new HashSet<SoftReference<Bitmap>>();
}

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

    // 不再被缓存的条目.
    @Override
    protected void entryRemoved(boolean evicted, String key,
            BitmapDrawable oldValue, BitmapDrawable newValue) {
        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
            // 被移除的条目是一个正在recycle的图片,所以通知其已经被从缓存中移除
            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
        } else {
            // 被移除条目是standard BitmapDrawable.
            if (Utils.hasHoneycomb()) {
                // 运行在Honeycomb及更高版本,所以把位图加到HashSet集合
                mReusableBitmaps.add
                        (new SoftReference<Bitmap>(oldValue.getBitmap()));
            }
        }
    }
....
}

使用已存在的位图
解码器检查是否有能直接用的已存在位图。例如
public static Bitmap decodeSampledBitmapFromFile(String filename,
        int reqWidth, int reqHeight, ImageCache cache) {

    final BitmapFactory.Options options = new BitmapFactory.Options();
    ...
    BitmapFactory.decodeFile(filename, options);
    ...

    // If we're running on Honeycomb or newer, try to use inBitmap.
    if (Utils.hasHoneycomb()) {
        addInBitmapOptions(options, cache);
    }
    ...
    return BitmapFactory.decodeFile(filename, options);
}
addInBitmapOptions()寻找一个已存在的位图为inBitmap。注意只有当它找到一个合适的匹配时(你的代码应当假设这个匹配可能不会发生)这个方法才会设置一个值。
private static void addInBitmapOptions(BitmapFactory.Options options,
        ImageCache cache) {
    // inBitmap only works with mutable bitmaps, so force the decoder to
    // return mutable bitmaps.
    options.inMutable = true;

    if (cache != null) {
        // Try to find a bitmap to use for inBitmap.
        Bitmap inBitmap = cache.getBitmapFromReusableSet(options);

        if (inBitmap != null) {
            // If a suitable bitmap has been found, set it as the value of
            // inBitmap.
            options.inBitmap = inBitmap;
        }
    }
}

// This method iterates through the reusable bitmaps, looking for one 
// to use for inBitmap:
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
        Bitmap bitmap = null;

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

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

            if (null != item && item.isMutable()) {
                // Check to see it the item can be used for inBitmap.
                if (canUseForInBitmap(item, options)) {
                    bitmap = item;

                    // Remove from reusable set so it can't be used again.
                    iterator.remove();
                    break;
                }
            } else {
                // Remove from the set if the reference has been cleared.
                iterator.remove();
            }
        }
    }
    return bitmap;
}

接下来,canUseForInBitmap()决定是否一个候选bitmap能够满足被inBitmap使用的尺寸标准。
private static boolean canUseForInBitmap(
        Bitmap candidate, BitmapFactory.Options targetOptions) {
    int width = targetOptions.outWidth / targetOptions.inSampleSize;
    int height = targetOptions.outHeight / targetOptions.inSampleSize;

    // Returns true if "candidate" can be used for inBitmap re-use with
    // "targetOptions".
    return candidate.getWidth() == width && candidate.getHeight() == height;
}









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值