专栏首页向治洪android bitmap的内存分配和优化

android bitmap的内存分配和优化

首先Bitmap在Android虚拟机中的内存分配,在Google的网站上给出了下面的一段话 

大致的意思也就是说,在Android3.0之前,Bitmap的内存分配分为两部分,一部分是分配在Dalvik的VM堆中,而像素数据的内存是分配在Native堆中,而到了Android3.0之后,Bitmap的内存则已经全部分配在VM堆上,这两种分配方式的区别在于,Native堆的内存不受Dalvik虚拟机的管理,我们想要释放Bitmap的内存,必须手动调用Recycle方法,而到了Android 3.0之后的平台,我们就可以将Bitmap的内存完全放心的交给虚拟机管理了,我们只需要保证Bitmap对象遵守虚拟机的GC Root Tracing的回收规则即可。OK,基础知识科普到此。接下来分几个要点来谈谈如何优化Bitmap内存问题。

针对3.0版本的优化方案,请看以下代码,

private int mCacheRefCount = 0;//缓存引用计数器
private int mDisplayRefCount = 0;//显示引用计数器
...
// 当前Bitmap是否被显示在UI界面上
public void setIsDisplayed(boolean isDisplayed) {
    synchronized (this) {
        if (isDisplayed) {
            mDisplayRefCount++;
            mHasBeenDisplayed = true;
        } else {
            mDisplayRefCount--;
        }
    }

    checkState();
}

//标记是否被缓存
public void setIsCached(boolean isCached) {
    synchronized (this) {
        if (isCached) {
            mCacheRefCount++;
        } else {
            mCacheRefCount--;
        }
    }

    checkState();
}

//用于检测Bitmap是否已经被回收
private synchronized void checkState() {
    if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
            && hasValidBitmap()) {
        getBitmap().recycle();
    }
}

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

通过引用计数的方法(mDisplayRefCount 与 mCacheRefCount)来追踪一个bitmap目前是否有被显示或者是在缓存中. 当下面条件满足时回收bitmap。

2.使用缓存,LruCache和DiskLruCache的结合 LruCache和DiskLruCache,大家一定不会陌生出于对性能和app的考虑,我们肯定是想着第一次从网络中加载到图片之后,能够将图片缓存在内存和sd卡中,这样,我们就不用频繁的去网络中加载图片,为了很好的控制内存问题,则会考虑使用LruCache作为Bitmap在内存中的存放容器,在sd卡则使用DiskLruCache来统一管理磁盘上的图片缓存。

3.SoftReference和inBitmap参数的结合 在第二点中提及到,可以采用LruCache作为存放Bitmap的容器,而在LruCache中有一个方法值得留意,那就是entryRemoved,按照文档给出的说法,在LruCache容器满了需要淘汰存放其中的对象腾出空间的时候会调用此方法(注意,这里只是对象被淘汰出LruCache容器,但并不意味着对象的内存会立即被Dalvik虚拟机回收掉),此时可以在此方法中将Bitmap使用SoftReference包裹起来,并用事先准备好的一个HashSet容器来存放这些即将被回收的Bitmap,有人会问,这样存放有什么意义?之所以会这样存放,还需要再提及到inBitmap参数(在Android3.0才开始有的,详情查阅API中的BitmapFactory.Options参数信息),这个参数主要是提供给我们进行复用内存中的Bitmap。

如果需要使用Bitmap的option参数还需要满足以下几个条件:

  • Bitmap一定要是可变的,即inmutable设置一定为ture;
  • Android4.4以下的平台,需要保证inBitmap和即将要得到decode的Bitmap的尺寸规格一致;
  • Android4.4及其以上的平台,只需要满足inBitmap的尺寸大于要decode得到的Bitmap的尺寸规格即可;

4.降低采样率,inSampleSize的计算

直接上代码

public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }

            long totalPixels = width / inSampleSize * height / inSampleSize ;

            final long totalReqPixelsCap = reqWidth * reqHeight * 2;

            while (totalPixels > totalReqPixelsCap) {
                inSampleSize *= 2;
                totalPixels /= 2;
            }
        }
        return inSampleSize;

5.采用decodeFileDescriptor来编码图片,比直接使用decodeFile更省内存

查看BitmapFactory的源码,对比一下两者的实现,可以发现decodeFile()最终是以流的方式生成bitmap 

decodeFile源码:

 public static Bitmap decodeFile(String pathName, Options opts) {  
     Bitmap bm = null;  
     InputStream stream = null;  
  try {  
         stream = new FileInputStream(pathName);  
         bm = decodeStream(stream, null, opts);  
     } catch (Exception e) {  
  /*  do nothing. 
             If the exception happened on open, bm will be null. 
         */ 
     } finally {  
  if (stream != null) {  
  try {  
                 stream.close();  
             } catch (IOException e) {  
  // do nothing here 
             }  
         }  
     }  
  return bm;  
 }  

decodeFileDescriptor的源码,可以找到native本地方法decodeFileDescriptor,通过底层生成bitmap

decodeFileDescriptor源码:

  public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {  
  if (nativeIsSeekable(fd)) {  
            Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);  
  if (bm == null && opts != null && opts.inBitmap != null) {  
  throw new IllegalArgumentException("Problem decoding into existing bitmap");  
            }  
  return finishDecode(bm, outPadding, opts);  
        } else {  
            FileInputStream fis = new FileInputStream(fd);  
  try {  
  return decodeStream(fis, outPadding, opts);  
            } finally {  
  try {  
                    fis.close();  
                } catch (Throwable t) {/* ignore */}  
            }  
        }  
    }  
  
 private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,Rect padding, Options opts);   

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 对Bitmap的内存优化

    在Android应用里,最耗费内存的就是图片资源。而且在Android系统中,读取位图Bitmap时,分给虚拟机中的图片的堆栈大小只有8M,如果超出了,就会出...

    xiangzhihong
  • android 优化之Bitmap优化

    Bitmap优化 一个进程的内存可以由2个部分组成:native和dalvik dalvik就是我们平常说的java堆,我们创建的对象是在这里面分配的,而bi...

    xiangzhihong
  • 自定义圆角和园边的实现

    本来想在网上找个圆角的例子看一看,不尽人意啊,基本都是官方的Demo的那张原理图,稍后会贴出。于是自己自定义了个View,实现图片的圆角以及圆形效果。效果图: ...

    xiangzhihong
  • 美图分布式Bitmap实践:Naix

    大数据技术和应用系统目前已经在各个行业中发挥着巨大的作用,各种各样的开源技术也给大数据从业人员带来了很大的便利。Bitmap 作为一种大数据需求下产生的计算体系...

    美图数据技术团队
  • Drawable.Bitmap.Canvas.Paint.Matrix

    由于对Drawable、Bitmap、Canvas、Paint和 Matrix 的关系和使用 一直不太清楚,就在网上搜集了一下,摘录一些,主要来看这两篇文章:D...

    晚晴幽草轩轩主
  • 对Bitmap的内存优化

    在Android应用里,最耗费内存的就是图片资源。而且在Android系统中,读取位图Bitmap时,分给虚拟机中的图片的堆栈大小只有8M,如果超出了,就会出...

    xiangzhihong
  • Android处理图像数据全记录

    Android中处理图像是一件很常见的事情,这里记录备忘一些亲身使用过的处理图片数据的方法。

    技术小黑屋
  • 【Rust日报】2020-02-02 Rust编译模型的灾难

    推荐一个学习设计模式的网站,春节在家自我隔离时系统的学习了设计模式,这个网站插图丰富,讲解比较细致,还可以购买电子版,支持PDF、 EPUB、 MOBI、 KF...

    MikeLoveRust
  • 百度 Deep Voice 实现文本到语音的实时转换;迄今最强核弹 GTX 1080 TI | 开发者头条

    ▲ 内容预览: 百度实现文本到语音的实时转换 Facebook 发布支持 90 种语言的预训练词向量 英伟达发布迄今为止最强核弹 GTX 1080 TI 每日...

    AI研习社
  • 计数计量单位KMGTPEZY【计算机】【天文】

    Int32 值类型表示值介于 -2,147,483,648 到 +2,147,483,647 之间的有符号整数。

    sunsky

扫码关注云+社区

领取腾讯云代金券