BitMap高效显示策略(三):使用内存缓存技术和BitmapFactory.Options.inBitmap参数

接上篇BitMap高效显示策略(二):在ListView上异步加载网络图片,ListView在屏幕上来回划动时,重新进入屏幕范围的Item会重新从网络上加载一次图片,这样做会降低效率,并且浪费流量,更好的方法是使用缓存,缓存可以分为2级:内存缓存和文件缓存,这篇只讨论内存缓存:当ListView需要在指定Item上加载图片时,先根据下载URL检查缓存中是否存在这个BitmapDrawable,如果存在,直接显示在ImageView上,不存在,则从网络上下载。当一个Bitmap下载完毕后,加入内存缓存。

按照以上思路,接上篇代码,改写loadImage方法,伪代码为:

[java]  view plain copy
  1. public void loadImage(String url, ImageView imageView) {  
  2.     if (url == null) {  
  3.         return;  
  4.     }  
  5.       
  6.     BitmapDrawable bitmapDrawable = null;  
  7.       
  8.     //先从内存缓存中读取  
  9.                if (缓存 != null) {  
  10.             bitmapDrawable = 缓存中获取bitmapDrawable方法(url);  
  11.                }  
  12.         
  13.                if (bitmapDrawable != null) {  
  14.             imageView.setImageDrawable(bitmapDrawable);  
  15.                }  
  16.                //否则下载  
  17.                else if (cancelPotentialWork(url, imageView)) {  
  18.   
  19.         final BitmapWorkerTask task = new BitmapWorkerTask(url, imageView);  
  20.         final AsyncDrawable asyncDrawable = new AsyncDrawable(mResources,  
  21.                 task);  
  22.         imageView.setImageDrawable(asyncDrawable);  
  23.   
  24.         task.executeOnExecutor(ImageAsyncTask.DUAL_THREAD_EXECUTOR);  
  25.     }  
  26. }  
同时,在BitmapWorkerTask的doInBackground中,在Bitmap加载完毕后将drawable加入缓存

[java]  view plain copy
  1. if (bitmap == null && !isCancelled()  
  2.         && getAttachedImageView() != null && !mExitTasksEarly) {  
  3.     bitmap = processBitmap(mUrl);  
  4. }  
  5.   
  6. if (bitmap != null) {  
  7.     drawable = new BitmapDrawable(mResources, bitmap);  
  8.     if (缓存 != null) {  
  9.         加入缓存  
  10.     }  
  11. }  

接下来,实现以上伪码

首先创建ImageCache这个类,ImageCache用于封装缓存对象,ImageCache内使用LruCache类作为内存缓存,LruCache内部实现中,使用了LinkedHashMap,具体实现分析可以参照android.util.LruCache主要代码分析 。

ImageCache的实现:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class ImageCache {
	private static final String TAG = "TEST";
	
	//缓存基本设置
	private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 5; // 内存缓存默认大小5MB
	private static final boolean DEFAULT_MEM_CACHE_ENABLED = true;
	
	//内存缓存
	private LruCache<String, BitmapDrawable> mMemoryCache;
	
	private ImageCacheParams mCacheParams;	
	
	public static class ImageCacheParams {
		//mem
		public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
		public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
		
		/***
		 * 手动设置内存缓存大小
		 * @param percent 缓存大小占最大可用内存的比例
		 */
		public void setMemCacheSizePercent(float percent) {
			if (percent < 0.01f || percent > 0.8f) {
				throw new IllegalArgumentException("setMemCacheSizePercent - percent must be "
						+ "between 0.01 and 0.8 (inclusive)");
			}
			memCacheSize = Math.round(percent * Runtime.getRuntime().maxMemory() / 1024);
		}
	}
	
	/**
	 * 
	 * 放在no UI的fragment中,保证屏幕旋转时不被回收
	 * @param fragmentManager
	 * @param cacheParams
	 * @return
	 */
	public static ImageCache getInstance(
			FragmentManager fragmentManager, ImageCacheParams cacheParams) {
		final RetainFragment mRetainFragment = findOrCreateRetainFragment(fragmentManager);
		ImageCache imageCache = (ImageCache) mRetainFragment.getObject();
		if (imageCache == null) {
            imageCache = new ImageCache(cacheParams);
            mRetainFragment.setObject(imageCache);
        }

        return imageCache;
	}
	
    private ImageCache(ImageCacheParams cacheParams) {
        init(cacheParams);
    }
    
    private void init(ImageCacheParams cacheParams) {
    	mCacheParams = cacheParams;
    	if (mCacheParams.memoryCacheEnabled) {

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

				@Override
				protected void entryRemoved(boolean evicted, String key,
						BitmapDrawable oldValue, BitmapDrawable newValue) {
					if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
						//减少缓存计数
						((RecyclingBitmapDrawable) oldValue).setIsCached(false);
					}
				}

				@Override
				protected int sizeOf(String key, BitmapDrawable bitmapDrawable) {
					final int bitmapSize = getBitmapSize(bitmapDrawable) / 1024;
					return bitmapSize == 0 ? 1 : bitmapSize;
				}
    			
    		};
    	}
    }
    
    /**
     * 清空缓存
     */
    public void clearCache() {
    	if (mMemoryCache != null) {
            mMemoryCache.evictAll();
        }
    }
    
    
    /**
     * 获取bitmap大小
     * @param bitmapDrawable
     * @return
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
	public static int getBitmapSize(BitmapDrawable bitmapDrawable) {
    	
    	Bitmap bitmap = bitmapDrawable.getBitmap();
    	//4.4
    	if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
    		return bitmap.getAllocationByteCount();
    	}
    	//3.1及以上
    	if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
    		return  bitmap.getByteCount();
    	}
    	//3.1之前的版本
    	return bitmap.getRowBytes() * bitmap.getHeight();
    }
    
    /**获取内存缓存中的图片
     * @param url
     * @return
     */
    public BitmapDrawable getBitmapFromMemCache(String url) {
    	BitmapDrawable bitmapDrawable = null;
    	
    	if (mMemoryCache != null) {
    		bitmapDrawable = mMemoryCache.get(url);
    	}
    	
    	if (bitmapDrawable != null) {
            Log.d("TEST", "Memory cache hit");
        }
    	
    	return bitmapDrawable;
    }
    
    /**
     * 图片加入内存缓存
     * @param data
     * @param value
     */
    public void addBitmapToCache(String url, BitmapDrawable bitmapDrawable) {
    	if (url == null || bitmapDrawable == null) {
    		return;
    	}
    	
    	if (mMemoryCache != null) {
    		//如果是RecyclingBitmapDrawable,增加缓存计数
    		if (RecyclingBitmapDrawable.class.isInstance(bitmapDrawable)) {
    			((RecyclingBitmapDrawable) bitmapDrawable).setIsCached(true);
    		}
    		
    		mMemoryCache.put(url, bitmapDrawable);
    	}
    }
	
	private static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
		RetainFragment mRetainFragment = (RetainFragment) fm.findFragmentByTag(ImageCache.class.getName());
		
		if (mRetainFragment == null) {
			mRetainFragment = new RetainFragment();
			fm.beginTransaction().add(mRetainFragment, 
					ImageCache.class.getName()).commitAllowingStateLoss();;
		}
		
		return mRetainFragment;
	}
	
	/**
	 * 后台Fragment用于在横竖屏切换时保存ImageCache对象。
	 *
	 */
	public static class RetainFragment extends Fragment {
		private Object object;
		

		public Object getObject() {
			return object;
		}


		public void setObject(Object object) {
			this.object = object;
		}

		@Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setRetainInstance(true);
        }
	}
}


LruCache<String, BitmapDrawable> mMemoryCache是ImageCache的核心。图片的Url作为LruCache的键,加载完后的BitmapDrawable作为值,以key/value的形式存储。

ImageCacheParams封装了缓存的一些设定,setMemCacheSizePercent方法设置内存缓存大小占应用程序最大可用内存的比例,如果不设置,默认大小是5MB。

ImageCache实例通过静态方法getInstance获取,ImageCache实例存储在RetainFragment中,RetainFragment是一个没有UI的Fragment,在onCreate方法中, 设置了setRetainInstance(true);所以这个Fragment在屏幕旋转时,不会重新创建,这样得以在配置变化时保存ImageCache对象并且Activity重新创建后快速加载缓存。

构造方法中调用了init方法,init方法中,初始化mMemoryCache对象, 重写了其entryRemoved 和sizeof方法,在entryRemoved中,减少RecyclingBitmapDrawable的缓存计数。sizeOf是用来计算单个缓存的BitmapDrawable对象的大小的,getBitmapSize中根据不同安卓版本,调用不同的计算方法。

[java]  view plain copy
  1. mImageWorker = new ImageFetcher(getActivity(), 50);  
  2.   
  3. ImageCache.ImageCacheParams cacheParams = new ImageCache.ImageCacheParams();  
  4.   
  5. //内存缓存大小为应用可用内存的1/4  
  6. cacheParams.setMemCacheSizePercent(0.25f);  
  7.   
  8. mImageWorker.initImageCache(getActivity().getSupportFragmentManager(), cacheParams);  
这样就可以使用缓存对象了。

修改 loadImage代码:

	public void loadImage(String url, ImageView imageView) {
		if (url == null) {
			return;
		}
		
		BitmapDrawable bitmapDrawable = null;
		
		//先从内存缓存中读取
        if (mImageCache != null) {
        	bitmapDrawable = mImageCache.getBitmapFromMemCache(url);
        }
        
        if (bitmapDrawable != null) {
        	imageView.setImageDrawable(bitmapDrawable);
        }
        //否则下载
        else if (cancelPotentialWork(url, imageView)) {

			final BitmapWorkerTask task = new BitmapWorkerTask(url, imageView);
			final AsyncDrawable asyncDrawable = new AsyncDrawable(mResources,
					task);
			imageView.setImageDrawable(asyncDrawable);

			task.executeOnExecutor(ImageAsyncTask.DUAL_THREAD_EXECUTOR);
		}
	}


修改BitmapWorkerTask的doInBackground:

[java]  view plain copy
  1.         @Override  
  2.         protected BitmapDrawable doInBackground(Void... params) {  
  3.   
  4.             Bitmap bitmap = null;  
  5.             BitmapDrawable drawable = null;  
  6.   
  7.             synchronized (mPauseWorkLock) {  
  8.                 while (mPauseWork && !isCancelled()) {  
  9.                     try {  
  10.                         mPauseWorkLock.wait();  
  11.                     } catch (InterruptedException e) {  
  12.                         e.printStackTrace();  
  13.                     }  
  14.                 }  
  15.             }  
  16.   
  17.             if (bitmap == null && !isCancelled()  
  18.                     && getAttachedImageView() != null && !mExitTasksEarly) {  
  19.                 bitmap = processBitmap(mUrl);  
  20.             }  
  21.   
  22.             if (bitmap != null) {  
  23.                 if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {  
  24.                                     drawable = new BitmapDrawable(mResources, bitmap);  
  25.                                 } else {  
  26.                                     // 3.0以下版本,创建自动回收的BitmapDrawable  
  27.                                     drawable = new RecyclingBitmapDrawable(mResources, bitmap);  
  28.                                 }  
  29.                   
  30.                 //加入缓存  
  31.                 if (mImageCache != null) {  
  32.                                     mImageCache.addBitmapToCache(mUrl, drawable);  
  33.                                 }   
  34.                 }  
  35.   
  36.             return drawable;  
  37.         }  
这样内存缓存就添加完成了。


BitmapFactory.Options.inBitmap:

在Android 3.0 引进了BitmapFactory.Options.inBitmap. 如果这个值被设置了,decode方法会在加载内容的时候去重用已经存在的bitmap. 这意味着bitmap的内存是被重新利用的,这样可以提升性能, 并且减少了内存的分配与回收。然而,使用inBitmap有一些限制。特别是在Android 4.4 之前,只支持同等大小的位图。

在ImageCache中加入全局变量:

private Set<SoftReference<Bitmap>> mReusableBitmaps;

这是个存放可重用Bitmap的软引用的集合

修改init方法:

[java]  view plain copy
  1. private void init(ImageCacheParams cacheParams) {  
  2.         mCacheParams = cacheParams;  
  3.         if (mCacheParams.memoryCacheEnabled) {  
  4.             //Android3.0以上可以重用bitmap内存,将内存缓存中的回收项重用。  
  5.             if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {  
  6.                 mReusableBitmaps =  
  7.                         Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());  
  8.             }  
  9.               
  10.             mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {  
  11.   
  12.                 @Override  
  13.                 protected void entryRemoved(boolean evicted, String key,  
  14.                         BitmapDrawable oldValue, BitmapDrawable newValue) {  
  15.                     if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {  
  16.                         //减少缓存计数  
  17.                         ((RecyclingBitmapDrawable) oldValue).setIsCached(false);  
  18.                     } else {  
  19.                         //加入待重用集合  
  20.                         if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {  
  21.                             mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));  
  22.                         }  
  23.                     }  
  24.                 }  
  25.   
  26.                 @Override  
  27.                 protected int sizeOf(String key, BitmapDrawable bitmapDrawable) {  
  28.                     final int bitmapSize = getBitmapSize(bitmapDrawable) / 1024;  
  29.                     return bitmapSize == 0 ? 1 : bitmapSize;  
  30.                 }  
  31.                   
  32.             };  
  33.         }  
  34.     }  
方法的开始,对当前Android版本进行了判断,如果是3.0以上,则初始化 mReusableBitmaps。在entryRemoved中,如果是3.0以上,将从缓存中移出的 Bitmap加入集合。

在方法中,decoder去检查是否有可用的bitmap。

[java]  view plain copy
  1. public static Bitmap decodeSampledBitmapFromStream(InputStream is,  
  2.         BitmapFactory.Options options, ImageCache cache) {  
  3.   
  4.     if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {  
  5.         addInBitmapOptions(options, cache);  
  6.     }  
  7.   
  8.     return BitmapFactory.decodeStream(is, null, options);  
  9. }  

addInBitmapOptions方法会去可重用的集合中查找一个合适的bitmap赋值给inBitmap,这个方法不保证一定返回非空的bitmap

[java]  view plain copy
  1. @TargetApi(Build.VERSION_CODES.HONEYCOMB)  
  2. private static void addInBitmapOptions(BitmapFactory.Options options,  
  3.         ImageCache cache) {  
  4.   
  5.     options.inMutable = true;  
  6.     if (cache != null) {  
  7.         Bitmap inBitmap = cache.getBitmapFromReusableSet(options);  
  8.   
  9.         if (inBitmap != null) {  
  10.             options.inBitmap = inBitmap;  
  11.         }  
  12.     }  
  13. }  

getBitmapFromReusableSet方法中,遍历集合中的每个Bitmap,如果有合适的就返回。

[java]  view plain copy
  1. protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {  
  2.     Bitmap bitmap = null;  
  3.   
  4.     if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {  
  5.         final Iterator<SoftReference<Bitmap>> iterator = mReusableBitmaps  
  6.                 .iterator();  
  7.         Bitmap item;  
  8.   
  9.         while (iterator.hasNext()) {  
  10.             item = iterator.next().get();  
  11.   
  12.             if (null != item && item.isMutable()) {  
  13.                 if (canUseForInBitmap(item, options)) {  
  14.                     bitmap = item;  
  15.   
  16.                     // Remove from reusable set so it can't be used again  
  17.                     iterator.remove();  
  18.                     break;  
  19.                 }  
  20.             } else {  
  21.                 // Remove from the set if the reference has been cleared.  
  22.                 iterator.remove();  
  23.             }  
  24.         }  
  25.     }  
  26.   
  27.     return bitmap;  
  28. }  

[java]  view plain copy
  1. @TargetApi(VERSION_CODES.KITKAT)  
  2. private static boolean canUseForInBitmap(Bitmap candidate,  
  3.         BitmapFactory.Options targetOptions) {  
  4.   
  5.     // 4.4之前的版本,尺寸必须完全吻合  
  6.     if (Build.VERSION.SDK_INT < VERSION_CODES.KITKAT) {  
  7.         return candidate.getWidth() == targetOptions.outWidth  
  8.                 && candidate.getHeight() == targetOptions.outHeight  
  9.                 && targetOptions.inSampleSize == 1;  
  10.     }  
  11.     // 4.4版本,可以使用比自己大的bitmap  
  12.     int width = targetOptions.outWidth / targetOptions.inSampleSize;  
  13.     int height = targetOptions.outHeight / targetOptions.inSampleSize;  
  14.   
  15.     // 根据图片格式,计算具体的bitmap大小  
  16.     int byteCount = width * height  
  17.             * getBytesPerPixel(candidate.getConfig());  
  18.   
  19.     return byteCount <= candidate.getAllocationByteCount();  
  20. }  
  21.   
  22. /** 
  23.  * Return the byte usage per pixel of a bitmap based on its configuration. 
  24.  *  
  25.  * @param config 
  26.  *            The bitmap configuration. 
  27.  * @return The byte usage per pixel. 
  28.  */  
  29. private static int getBytesPerPixel(Config config) {  
  30.     if (config == Config.ARGB_8888) {  
  31.         return 4;  
  32.     } else if (config == Config.RGB_565) {  
  33.         return 2;  
  34.     } else if (config == Config.ARGB_4444) {  
  35.         return 2;  
  36.     } else if (config == Config.ALPHA_8) {  
  37.         return 1;  
  38.     }  
  39.     return 1;  
  40. }  

完整的ImageCache代码:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class ImageCache {
	private static final String TAG = "TEST";
	
	//缓存基本设置
	private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 5; // 内存缓存默认大小5MB
	private static final boolean DEFAULT_MEM_CACHE_ENABLED = true;
	
	//内存缓存
	private LruCache<String, BitmapDrawable> mMemoryCache;
	
	private ImageCacheParams mCacheParams;
	
	//3.0后的bitmap重用机制
	private Set<SoftReference<Bitmap>> mReusableBitmaps;
	
	
	public static class ImageCacheParams {
		//mem
		public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
		public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
		
		/***
		 * 手动设置内存缓存大小
		 * @param percent 缓存大小占最大可用内存的比例
		 */
		public void setMemCacheSizePercent(float percent) {
			if (percent < 0.01f || percent > 0.8f) {
				throw new IllegalArgumentException("setMemCacheSizePercent - percent must be "
						+ "between 0.01 and 0.8 (inclusive)");
			}
			memCacheSize = Math.round(percent * Runtime.getRuntime().maxMemory() / 1024);
		}
	}
	
	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()) {
					if (canUseForInBitmap(item, options)) {
						
						Log.v("TEST", "canUseForInBitmap!!!!");
						
                        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;
	}
	
	@TargetApi(VERSION_CODES.KITKAT)
	private static boolean canUseForInBitmap(
			Bitmap candidate, BitmapFactory.Options targetOptions) {
		
		//4.4之前的版本,尺寸必须完全吻合
		if (Build.VERSION.SDK_INT < VERSION_CODES.KITKAT) {
			return candidate.getWidth() == targetOptions.outWidth
                    && candidate.getHeight() == targetOptions.outHeight
                    && targetOptions.inSampleSize == 1;
		}
		//4.4版本,可以使用比自己大的bitmap
		int width = targetOptions.outWidth / targetOptions.inSampleSize;
		int height = targetOptions.outHeight / targetOptions.inSampleSize;
		
		//根据图片格式,计算具体的bitmap大小
		int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
		
		return byteCount <= candidate.getAllocationByteCount();
	}
	
	/**
     * Return the byte usage per pixel of a bitmap based on its configuration.
     * @param config The bitmap configuration.
     * @return The byte usage per pixel.
     */
    private static int getBytesPerPixel(Config config) {
        if (config == Config.ARGB_8888) {
            return 4;
        } else if (config == Config.RGB_565) {
            return 2;
        } else if (config == Config.ARGB_4444) {
            return 2;
        } else if (config == Config.ALPHA_8) {
            return 1;
        }
        return 1;
    }
	
	/**
	 * 
	 * 放在no UI的fragment中,保证屏幕旋转时不被回收
	 * @param fragmentManager
	 * @param cacheParams
	 * @return
	 */
	public static ImageCache getInstance(
			FragmentManager fragmentManager, ImageCacheParams cacheParams) {
		final RetainFragment mRetainFragment = findOrCreateRetainFragment(fragmentManager);
		ImageCache imageCache = (ImageCache) mRetainFragment.getObject();
		if (imageCache == null) {
            imageCache = new ImageCache(cacheParams);
            mRetainFragment.setObject(imageCache);
        }

        return imageCache;
	}
	
    private ImageCache(ImageCacheParams cacheParams) {
        init(cacheParams);
    }
    
    private void init(ImageCacheParams cacheParams) {
    	mCacheParams = cacheParams;
    	if (mCacheParams.memoryCacheEnabled) {
    		//Android3.0以上可以重用bitmap内存,将内存缓存中的回收项重用。
    		if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
    			mReusableBitmaps =
                        Collections.synchronizedSet(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)) {
						//减少缓存计数
						((RecyclingBitmapDrawable) oldValue).setIsCached(false);
					} else {
						//加入待重用集合
						if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
							mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
						}
					}
				}

				@Override
				protected int sizeOf(String key, BitmapDrawable bitmapDrawable) {
					final int bitmapSize = getBitmapSize(bitmapDrawable) / 1024;
					return bitmapSize == 0 ? 1 : bitmapSize;
				}
    			
    		};
    	}
    }
    
    /**
     * 清空缓存
     */
    public void clearCache() {
    	if (mMemoryCache != null) {
            mMemoryCache.evictAll();
        }
    }
    
    
    /**
     * 获取bitmap大小
     * @param bitmapDrawable
     * @return
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
	public static int getBitmapSize(BitmapDrawable bitmapDrawable) {
    	
    	Bitmap bitmap = bitmapDrawable.getBitmap();
    	//4.4
    	if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
    		return bitmap.getAllocationByteCount();
    	}
    	//3.1及以上
    	if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
    		return  bitmap.getByteCount();
    	}
    	//3.1之前的版本
    	return bitmap.getRowBytes() * bitmap.getHeight();
    }
    
    /**获取内存缓存中的图片
     * @param url
     * @return
     */
    public BitmapDrawable getBitmapFromMemCache(String url) {
    	BitmapDrawable bitmapDrawable = null;
    	
    	if (mMemoryCache != null) {
    		bitmapDrawable = mMemoryCache.get(url);
    	}
    	
    	if (bitmapDrawable != null) {
            Log.d("TEST", "Memory cache hit");
        }
    	
    	return bitmapDrawable;
    }
    
    /**
     * 图片加入内存缓存
     * @param data
     * @param value
     */
    public void addBitmapToCache(String url, BitmapDrawable bitmapDrawable) {
    	if (url == null || bitmapDrawable == null) {
    		return;
    	}
    	
    	//先加入内存缓存
    	if (mMemoryCache != null) {
    		//如果是RecyclingBitmapDrawable,增加缓存计数
    		if (RecyclingBitmapDrawable.class.isInstance(bitmapDrawable)) {
    			((RecyclingBitmapDrawable) bitmapDrawable).setIsCached(true);
    		}
    		
    		mMemoryCache.put(url, bitmapDrawable);
    	}
    }
	
	private static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
		RetainFragment mRetainFragment = (RetainFragment) fm.findFragmentByTag(ImageCache.class.getName());
		
		if (mRetainFragment == null) {
			mRetainFragment = new RetainFragment();
			fm.beginTransaction().add(mRetainFragment, 
					ImageCache.class.getName()).commitAllowingStateLoss();;
		}
		
		return mRetainFragment;
	}
	
	/**
	 * 后台Fragment用于在横竖屏切换时保存ImageCache对象。
	 *
	 */
	public static class RetainFragment extends Fragment {
		private Object object;
		

		public Object getObject() {
			return object;
		}


		public void setObject(Object object) {
			this.object = object;
		}

		@Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setRetainInstance(true);
        }
	}
}

Demo下载地址:

http://download.csdn.net/detail/ohehehou/8137871
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值