在Android2.3或更低版本中,推荐使用recycle()
方法。如果在你的应用中需要显示大量的图片,那么你的应用很有可能出现OutOfMemoryError 的错误。调用 recycle()
方法能让应用尽可能快的回收内存资源。
recycle()
方法。如果你调用recycle(),随后就尝试去画这张图,你将会得到"Canvas: trying to use a recycled bitmap" 的错误 。
mDisplayRefCount
and mCacheRefCount
)来追踪一张Bitmap是正在被显示还是存储在内存缓存中,代码回收Bitmap需要符合下面的几个条件:
mDisplayRefCount
和 mCacheRefCount
的引用计数都为0private 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(); }
Options
对象作为参数的decode方法会试图重用已存在的Bitmap。这意味着Bitmap的内存资源被重用了,从而使得程序性能得到提高,并且省去了这份内存的分配和回收工作。下面是使用inBitmap 属性的一些说明和注意点:
HashSet<SoftReference<Bitmap>> mReusableBitmaps; private LruCache<String, BitmapDrawable> mMemoryCache; // If you're running on Honeycomb or newer, create // a HashSet of references to reusable bitmaps. if (Utils.hasHoneycomb()) { mReusableBitmaps = new HashSet<SoftReference<Bitmap>>(); } mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) { //Notify the removed entry that is no longer being cached. @Override protected void entryRemoved(boolean evicted, String key, BitmapDrawable oldValue, BitmapDrawable newValue) { if (RecyclingBitmapDrawable.class.isInstance(oldValue)) { // The removed entry is a recycling drawable, so notify it // that it has been removed from the memory cache. ((RecyclingBitmapDrawable) oldValue).setIsCached(false); } else { // The removed entry is a standard BitmapDrawable. if (Utils.hasHoneycomb()) { // We're running on Honeycomb or later, so add the bitmap // to a SoftReference set for possible use with inBitmap later. 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是否设置了一张已经存在的Bitmap ,注意这个方法只有在它找到一个合适的匹配图片才会设置inBitmap的值(你永远都不要去假设相匹配的图片一定找得到)。
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; }
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; }