Managing Bitmap Memory(管理位图内存)

In addition to the steps described in Caching Bitmaps, there are specific things you can do to facilitate garbage collection and bitmap reuse.

除了在Caching Bitmaps描述的步骤,不需要具体东西你去催促垃圾和位图再使用。

The recommended strategy depends on which version(s) of Android you are targeting.

推荐的策略是依靠你的目标andorid版本。

The BitmapFun sample app included with this class shows you how to design your app to work efficiently across different versions of Android.

BitmapFun示例应用包含这个类展示给你如何设计你的应用在不同版本的android下工作。

To set the stage for this lesson, here is how Android's management of bitmap memory has evolved:

通过这节课,android管理位图内存是怎么样演变的。

  • On Android Android 2.2 (API level 8) and lower, when garbage collection occurs, your app's threads get stopped. This causes a lag that can degrade performance. Android 2.3 adds concurrent garbage collection, which means that the memory is reclaimed soon after a bitmap is no longer referenced.
  • 在android2.2版本之前,当GC运行,你的应用线程会停止,这个情况会停滞降低性能,android2.3添加并发GC意味着当位图不在长时间引用时会不久就会被回收。
  • On Android 2.3.3 (API level 10) and lower, the backing pixel data for a bitmap is stored in native memory. It is separate from the bitmap itself, which is stored in the Dalvik heap. The pixel data in native memory is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash. As of Android 3.0 (API Level 11), the pixel data is stored on the Dalvik heap along with the associated bitmap.
  • 在android2.3.3之前,位图的后台像素数据存在本地内存。和位图分开的,存在Dalvik的堆里。像素数据在本地内存不能被回收在已知方法中,使应用容易突破它的内存界限并且崩溃的潜在诱因,android 3.0开始,像素数据和相应的位图一起储存在Dalvik堆中。

The following sections describe how to optimize bitmap memory management for different Android versions.

接下来的这节课描述怎样最优化的管理位图内存在不同的android版本上。

Manage Memory on Android 2.3.3 and Lower(管理内存在android2.3.3之前)


On Android 2.3.3 (API level 10) and lower, using recycle() is recommended. 

在android2.3.3和之前的版本,推荐使用recycle()。

If you're displaying large amounts of bitmap data in your app, you're likely to run into OutOfMemoryError errors. The recycle() method allows an app to reclaim memory as soon as possible.

如果你显示大量的位图数据在你的应用,你可能出现内存溢出错误,recycle()方法允许应用尽可能的回收内存。

Caution: You should use recycle() only when you are sure that the bitmap is no longer being used. If you call recycle() and later attempt to draw the bitmap, you will get the error: "Canvas: trying to use a recycled bitmap".

警告: 只有当你确定位图长时间不会在被使用你才能使用recycle(),如果你调用recycle()而且之后试图绘制位图,你将会得到一个错误"试图使用已回收的位图"

The following code snippet gives an example of calling recycle(). It uses reference counting (in the variablesmDisplayRefCount and mCacheRefCount) to track whether a bitmap is currently being displayed or in the cache. The code recycles the bitmap when these conditions are met:

下面代码片段是调用recycle()的例子。使用参考包括追踪目前被显示或在缓存中的位图,代码回收位图当遇到这些状况。

  • The reference count for both mDisplayRefCount and mCacheRefCount is 0.
  • 参考mDisplayRefCount和mCacheRefCount都为0。
  • The bitmap is not null, and it hasn't been recycled yet
  • 位图不为null,而且已经被回收。
[java]  view plain copy
  1. private int mCacheRefCount = 0;  
  2. private int mDisplayRefCount = 0;  
  3. ...  
  4. // Notify the drawable that the displayed state has changed.  
  5. // 通知drawable显示状况已经改变  
  6. // Keep a count to determine when the drawable is no longer displayed.  
  7. // 保证数量确定当drawable不在显示  
  8. public void setIsDisplayed(boolean isDisplayed) {  
  9.     synchronized (this) {  
  10.         if (isDisplayed) {  
  11.             mDisplayRefCount++;  
  12.             mHasBeenDisplayed = true;  
  13.         } else {  
  14.             mDisplayRefCount--;  
  15.         }  
  16.     }  
  17.     // Check to see if recycle() can be called.  
  18.     // 查看recycle()是否已经被调用  
  19.     checkState();  
  20. }  
  21.   
  22. // Notify the drawable that the cache state has changed.  
  23. // 通知drawable缓存状态已经改变  
  24. // Keep a count to determine when the drawable is no longer being cached.  
  25. // 保证数量确定当drawable不再被缓存  
  26. public void setIsCached(boolean isCached) {  
  27.     synchronized (this) {  
  28.         if (isCached) {  
  29.             mCacheRefCount++;  
  30.         } else {  
  31.             mCacheRefCount--;  
  32.         }  
  33.     }  
  34.     // Check to see if recycle() can be called.  
  35.     // 查看recycle()是否已经被调用  
  36.     checkState();  
  37. }  
  38.   
  39. private synchronized void checkState() {  
  40.     // If the drawable cache and display ref counts = 0, and this drawable  
  41.     // has been displayed, then recycle.  
  42.     // 如果drawable mCacheRefCount和mDisplayRefCount都为0,那么要么drawable被显示要么被回收   
  43.     if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed  
  44.             && hasValidBitmap()) {  
  45.         getBitmap().recycle();  
  46.     }  
  47. }  
  48.   
  49. private synchronized boolean hasValidBitmap() {  
  50.     Bitmap bitmap = getBitmap();  
  51.     return bitmap != null && !bitmap.isRecycled();  
  52. }  


Manage Memory on Android 3.0 and Higher(管理缓存在android3.0或更高版本)


Android 3.0 (API Level 11) introduces the BitmapFactory.Options.inBitmap field. If this option is set, decode methods that take the Options object will attempt to reuse an existing bitmap when loading content.

android 3.0引入BitmapFactory.Options.inBitmap领域,如果这个选项被设置使用Options对象解码方法将会尝试重新使用一个现存的位图当加载内容时。

This means that the bitmap's memory is reused, resulting in improved performance, and removing both memory allocation and de-allocation. There are some caveats in using inBitmap:

这意味着位图的内存可以被再次使用,从而提高性能,而且移除已分配的内存并且释放,这里有一些注意在使用inBitmap的时候。

  • The reused bitmap must be of the same size as the source content (to make sure that the same amount of memory is used), and in JPEG or PNG format (whether as a resource or as a stream).
  • 返回的位图一定是和原来的内容尺寸一样(确保内存使用量是一样的)。而且是JPEG或者PNG格式(无罗是资源还是流)

  • The configuration of the reused bitmap overrides the setting of inPreferredConfig, if set.
  • 返回的位图会重新设置inPreferredConfig.
  • You should always use the returned bitmap of the decode method, because you can't assume that reusing the bitmap worked (for example, if there is a size mismatch).
  • 你一定经常使用解码方法返回位图,因为你不肯定重新使用的位图可以使用(例如尺寸不合适)。

    Save a bitmap for later use(保存一个位图稍后使用)

    The following snippet demonstrates how an existing bitmap is stored for possible later use in the sample app.

    下面的片段说明一个一直存在的位图被储存可能稍后被使用在相同的应用里。

    When an app is running on Android 3.0 or higher and a bitmap is evicted from the LruCache, a soft reference to the bitmap is placed in a HashSet, for possible reuse later with inBitmap:

    当应用运行在android3.0或者更高的版本里并且位图被LruCache回收,一个位图的软引用被放置在一个HashSet,为了inBitmap稍后可能重新使用:

    [java]  view plain copy
    1. HashSet<SoftReference<Bitmap>> mReusableBitmaps;  
    2. private LruCache<String, BitmapDrawable> mMemoryCache;  
    3.   
    4. // If you're running on Honeycomb or newer, create  
    5. // a HashSet of references to reusable bitmaps.  
    6. // 如果你在3.0或更高版本,创建一个HashSet引用来重新使用bitmaps  
    7. if (Utils.hasHoneycomb()) {  
    8.     mReusableBitmaps = new HashSet<SoftReference<Bitmap>>();  
    9. }  
    10.   
    11. mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {  
    12.   
    13.     // Notify the removed entry that is no longer being cached.  
    14.     // 通知移除标记为不再缓存  
    15.     @Override  
    16.     protected void entryRemoved(boolean evicted, String key,  
    17.             BitmapDrawable oldValue, BitmapDrawable newValue) {  
    18.         if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {  
    19.             // The removed entry is a recycling drawable, so notify it  
    20.             // that it has been removed from the memory cache.  
    21.         // 移除标记是回收的drawable,所以通知内存它已经被回收  
    22.             ((RecyclingBitmapDrawable) oldValue).setIsCached(false);  
    23.         } else {  
    24.             // The removed entry is a standard BitmapDrawable.  
    25.         // 删除标记为标准的BitmapDrawable  
    26.             if (Utils.hasHoneycomb()) {  
    27.                 // We're running on Honeycomb or later, so add the bitmap  
    28.                 // to a SoftReference set for possible use with inBitmap later.  
    29.         // 运行在3.0以后的版本,添加位图到软引用供inBitmap稍后可能使用  
    30.                 mReusableBitmaps.add  
    31.                         (new SoftReference<Bitmap>(oldValue.getBitmap()));  
    32.             }  
    33.         }  
    34.     }  
    35. ....  
    36. }  

Use an existing bitmap(使用一个持续存在的位图)

In the running app, decoder methods check to see if there is an existing bitmap they can use. For example:
在运行中的应用中,解码方法检查如果这是一个存在的位图他们使用,例如:

[java]  view plain copy
  1. public static Bitmap decodeSampledBitmapFromFile(String filename,  
  2.         int reqWidth, int reqHeight, ImageCache cache) {  
  3.   
  4.     final BitmapFactory.Options options = new BitmapFactory.Options();  
  5.     ...  
  6.     BitmapFactory.decodeFile(filename, options);  
  7.     ...  
  8.   
  9.     // If we're running on Honeycomb or newer, try to use inBitmap.  
  10.     // 如果在3.0或更新的版本试着使用inBitmap  
  11.     if (Utils.hasHoneycomb()) {  
  12.         addInBitmapOptions(options, cache);  
  13.     }  
  14.     ...  
  15.     return BitmapFactory.decodeFile(filename, options);  
  16. }  

The next snippet shows the addInBitmapOptions() method that is called in the above snippet.
下一个片段展视addInBitmapOptions()方法调用上面的片段。
It looks for an existing bitmap to set as the value for inBitmap. Note that this method only sets a value for inBitmap if it finds a suitable match (your code should never assume that a match will be found):
寻找存在的位图来设置InBitmap的常数。注意这个方法只会设置inBitmap的数值如果它找到合适的相匹配的(你的代码不应该假定匹配的对象将会被找到):

[java]  view plain copy
  1. private static void addInBitmapOptions(BitmapFactory.Options options,  
  2.         ImageCache cache) {  
  3.     // inBitmap only works with mutable bitmaps, so force the decoder to  
  4.     // return mutable bitmaps.  
  5.     // inBitmap只会处理可变的位图,所以强行解码返回可变的位图  
  6.     options.inMutable = true;  
  7.   
  8.     if (cache != null) {  
  9.         // Try to find a bitmap to use for inBitmap.  
  10.     // 试着使用inBitmap来找到位图  
  11.         Bitmap inBitmap = cache.getBitmapFromReusableSet(options);  
  12.   
  13.         if (inBitmap != null) {  
  14.             // If a suitable bitmap has been found, set it as the value of  
  15.             // inBitmap.  
  16.         // 如果合适的位图被找到,设置他为inBitmap的常数  
  17.             options.inBitmap = inBitmap;  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. // This method iterates through the reusable bitmaps, looking for one   
  23. // to use for inBitmap:  
  24. // 这个方法遍历循环使用的位图,寻找一个给InBitmap使用  
  25. protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {  
  26.         Bitmap bitmap = null;  
  27.   
  28.     if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {  
  29.         final Iterator<SoftReference<Bitmap>> iterator  
  30.                 = mReusableBitmaps.iterator();  
  31.         Bitmap item;  
  32.   
  33.         while (iterator.hasNext()) {  
  34.             item = iterator.next().get();  
  35.   
  36.             if (null != item && item.isMutable()) {  
  37.                 // Check to see it the item can be used for inBitmap.  
  38.         // 查看成员是否被inBitmap使用  
  39.                 if (canUseForInBitmap(item, options)) {  
  40.                     bitmap = item;  
  41.   
  42.                     // Remove from reusable set so it can't be used again.  
  43.             // 移除可循环使用设置不在使用  
  44.                     iterator.remove();  
  45.                     break;  
  46.                 }  
  47.             } else {  
  48.                 // Remove from the set if the reference has been cleared.  
  49.         // 设置移除如果引用已经被清除  
  50.                 iterator.remove();  
  51.             }  
  52.         }  
  53.     }  
  54.     return bitmap;  
  55. }  

Finally, this method determines whether a candidate bitmap satisfies the size criteria to be used for inBitmap:
最后,这个方法决定是否是一个适合的位图符合标准尺寸被inBitmap使用:

[java]  view plain copy
  1. private static boolean canUseForInBitmap(  
  2.         Bitmap candidate, BitmapFactory.Options targetOptions) {  
  3.     int width = targetOptions.outWidth / targetOptions.inSampleSize;  
  4.     int height = targetOptions.outHeight / targetOptions.inSampleSize;  
  5.   
  6.     // Returns true if "candidate" can be used for inBitmap re-use with  
  7.     // "targetOptions".  
  8.     return candidate.getWidth() == width && candidate.getHeight() == height;  
  9. }  




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值