Bitmap内存管理

在Android 2.3.3,使用recycle()来释放Bitmap占用的内存,但是你必须要等到该Bitmap不再使用时,才可以使用recyle()来释放Bitma占用的内存,否则的话会引起错误。
我们经常会使用到同一张图片,而Bitmap的创建和销毁是比较占用内存的开销的,所以我们可以自定义一个可复用的BitmapDrawable来管理Bitmap,并且赋予它两个变量,一个的mDisplayRefCount,一个是mCacheRefCount,初始化为0。当以下两个条件都满足的时候,那么就可以回收该Bitmap所占用的内存。这两个条件是:
1、mDisplayRefCount和mCacheRefCount同时为0。
2、Bitmap不为null,而且还没有被回收。
以下代码摘自Android Developer。
privateint mCacheRefCount =0;
privateint mDisplayRefCount =0;
...
//通知该BitmapDrawable,显示状态已改变
publicvoid setIsDisplayed(boolean isDisplayed){
synchronized(this){
if(isDisplayed){
mDisplayRefCount++;
mHasBeenDisplayed =true;
}else{
mDisplayRefCount--;
}
}
//看看是否满足回收条件
checkState();
}

//通知该BitmapDrawable,缓存状态已改变
publicvoid setIsCached(boolean isCached){
synchronized(this){
if(isCached){
mCacheRefCount++;
}else{
mCacheRefCount--;
}
}
//看看是否满足回收条件
checkState();
}

privatesynchronizedvoid checkState(){
if(mCacheRefCount <=0&& mDisplayRefCount <=0&& mHasBeenDisplayed
&& hasValidBitmap()){
getBitmap().recycle();
}
}

privatesynchronizedboolean hasValidBitmap(){
Bitmap bitmap = getBitmap();
return bitmap !=null&&!bitmap.isRecycled();
}
在Android3.0 之后,官方推荐使用BitmapFactory.Options.inBitmap这个字段来管理Bitmap的内存,该字段被使用后,那么Bitmap的解码方法将会使用Options来复用已经存在的Bitmap的内存空间,这将会提高App的性能。但是在使用inBitmap之前,我们要知道一些使用上的限制条件:
1、在Android4.4 (API 19)之前,使用的图片的宽高必须和被替换的Bitmap的宽高完全相同,而且使用的图片的Options.inSampleSize必须要为1。
2、在Android4.4 (API 19) 之后,使用的图片大小必须小于等于被替换的Bitmap的大小。
当一个应用运行在Android 3.0或者是更高的版本时,一张Bitmap的图片从LruCache中被移除的时候,那么该图片的软引用就会添加到一个HashSet中,以便该图片能够使用inBitmap进行复用。
我们看下面的代码(摘选自Android Developer)
Set<SoftReference<Bitmap>> mReusableBitmaps;
privateLruCache<String,BitmapDrawable> mMemoryCache;

//保证应用是运行在Android 3.0系统以上时,创建一个放置可复用Bitmap的同步HashSet
if(Utils.hasHoneycomb()){
mReusableBitmaps =
Collections.synchronizedSet(newHashSet<SoftReference<Bitmap>>());
}

mMemoryCache =newLruCache<String,BitmapDrawable>(mCacheParams.memCacheSize){
//当Bitmap从LruCache中被移除时,该回调方法被调用
@Override
protectedvoid entryRemoved(boolean evicted,String key,
BitmapDrawable oldValue,BitmapDrawable newValue){
if(RecyclingBitmapDrawable.class.isInstance(oldValue)){
//如果被移除的实例属于RecylingBitmapDrawable这个类,那么通知它,它已经从LruCache被移出了
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
}else{
//如果被移除的实例属于一个标准的BitmapDrawable,而且应用是运行在Android 3.0系统以上
//再次判断是否运行在Android 3.0系统以上是保证mResuableBitmaps不为空
if(Utils.hasHoneycomb()){
//将该Bitmap的软引用添加到HashSet中,以便以后使用inBitmap可以复用内存空间
mReusableBitmaps.add
(newSoftReference<Bitmap>(oldValue.getBitmap()));
}
}
}
....
}
当我们要从文件或者资源解码Bitmap的时候,我们可以使用下面的解码方法来查看是否存在可以复用的Bitmap内存。以下代码摘自Android Developer。
publicstaticBitmap decodeSampledBitmapFromFile(String filename,
int reqWidth,int reqHeight,ImageCache cache){

finalBitmapFactory.Options options =newBitmapFactory.Options();
...
BitmapFactory.decodeFile(filename, options);
...
//当运行在Android 3.0系统以上的时候,我们才可以使用inBitmap
if(Utils.hasHoneycomb()){
addInBitmapOptions(options, cache);
}
...
returnBitmapFactory.decodeFile(filename, options);
}
addInBitmapOptions方法将从HashSet中寻找是否存在可以复用的Bitmap。在使用该方法返回的是一个Bitmap,但是该Bitmap有可能为空,所以我们必须要判断一下返回的Bitmap是否为空,否则会出现空指针异常。
privatestaticvoid addInBitmapOptions(BitmapFactory.Options options,
ImageCache cache){
//inBitmap仅仅作用于可重用的Bitmap,所以这里我们强制让解码器给我们返回一个可重用的Bitmap
options.inMutable =true;

if(cache !=null){
//尝试从可复用的HashSet中寻找可复用的Bitmap内存
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;
}
}
}
//从可复用的Bitmaps中遍历,寻找是否存在有可以复用的inBitmap
protectedBitmap getBitmapFromReusableSet(BitmapFactory.Options options){
Bitmap bitmap =null;

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

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

if(null!= item && item.isMutable()){
//查看该item是否可以作为inBitmap
if(canUseForInBitmap(item, options)){
bitmap = item;

//从可复用的HashSet中删除item的软引用,让它不能再作为inBitmap
iterator.remove();
break;
}
}else{
//当该Item为空或者不是可变的时候,移除该item的软引用
iterator.remove();
}
}
}
}
return bitmap;
}
最后地,该方法决定了candidate是否符合inBitmap的条件。
staticboolean canUseForInBitmap(
Bitmap candidate,BitmapFactory.Options targetOptions){

if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.KITKAT){
//从Android 4.4系统开始,新的Bitmap的大小可以小于等于可复用的Bitmap
int width = targetOptions.outWidth / targetOptions.inSampleSize;
int height = targetOptions.outHeight / targetOptions.inSampleSize;
int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
return byteCount <= candidate.getAllocationByteCount();
}
//但是在Android 4.4系统以下,新的Bitmap的尺寸必须要刚好等于可复用的Bitmap,而且Options的inSamleSize的大小必须为1
return candidate.getWidth()== targetOptions.outWidth
&& candidate.getHeight()== targetOptions.outHeight
&& targetOptions.inSampleSize ==1;
}

/**
* 根据Bitmap的配置返回该Bitmap的每个像素占用多少个字节。
*/
staticint getBytesPerPixel(Config config){
if(config ==Config.ARGB_8888){
return4;
}elseif(config ==Config.RGB_565){
return2;
}elseif(config ==Config.ARGB_4444){
return2;
}elseif(config ==Config.ALPHA_8){
return1;
}
return1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值