目录介绍

  • 01.如何计算Bitmap占用内存

    • 1.1 如何计算占用内存
    • 1.2 上面方法计算内存对吗
    • 1.3 一个像素占用多大内存
  • 02.Bitmap常见四种颜色格式
    • 2.1 什么是bitmap
    • 2.2 Android常见是那种
    • 2.3 常见四种颜色格式介绍
    • 2.4 Bitmap到底有几种颜色格式
  • 03.Bitmap压缩技术
    • 3.1 质量压缩
    • 3.2 采样率压缩
    • 3.3 缩放法压缩
  • 04.Bitmap回收问题
    • 4.1 recycle()方法
    • 4.2 缓存原理
    • 4.3 Bitmap的复用
  • 05.Bitmap常见操作
    • 5.1 Bitmap的压缩方式
    • 5.2 Bitmap如何复用
    • 5.3 Bitmap使用API获取内存
    • 5.4 该博客对应测试项目地址

好消息

01.如何计算Bitmap占用内存

1.1 如何计算占用内存

  • 如果图片要显示下Android设备上,ImageView最终是要加载Bitmap对象的,就要考虑单个Bitmap对象的内存占用了,如何计算一张图片的加载到内存的占用呢?其实就是所有像素的内存占用总和:
  • bitmap内存大小 = 图片长度 x 图片宽度 x 单位像素占用的字节数
  • 起决定因素就是最后那个参数了,Bitmap'常见有2种编码方式:ARGB_8888和RGB_565,ARGB_8888每个像素点4个byte,RGB_565是2个byte,一般都采用ARGB_8888这种。那么常见的1080*1920的图片内存占用就是:1920 x 1080 x 4 = 7.9M

1.2 上面方法计算内存对吗

  • 我看到好多博客都是这样计算的,但是这样算对吗?有没有哥们试验过这种方法正确性?我觉得看博客要对博主表示怀疑,论证别人写的是否正确。更多详细可以看我的GitHub:https://github.com/yangchong211

    • 说出我的结论:上面1.1这种说法也对,但是不全对,没有说明场景,同时也忽略了一个影响项:Density。接下来看看源代码。
    • inDensity默认为图片所在文件夹对应的密度;inTargetDensity为当前系统密度。
    • 加载一张本地资源图片,那么它占用的内存 = width * height * nTargetDensity/inDensity * nTargetDensity/inDensity * 一个像素所占的内存。
    @Nullable
    public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
    @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
    validate(opts);
    if (opts == null) {
    opts = new Options();
    } if (opts.inDensity == 0 && value != null) {
    final int density = value.density;
    if (density == TypedValue.DENSITY_DEFAULT) {
    opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
    } else if (density != TypedValue.DENSITY_NONE) {
    opts.inDensity = density;
    }
    } if (opts.inTargetDensity == 0 && res != null) {
    opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
    } return decodeStream(is, pad, opts);
    }
  • 正确说法,这个注意呢?计算公式如下所示
    • 对资源文件:width * height * nTargetDensity/inDensity * nTargetDensity/inDensity * 一个像素所占的内存;
    • 别的:width * height * 一个像素所占的内存;

1.3 一个像素占用多大内存

  • Bitmap.Config用来描述图片的像素是怎么被存储的?

    • ARGB_8888: 每个像素4字节. 共32位,默认设置。
    • Alpha_8: 只保存透明度,共8位,1字节。
    • ARGB_4444: 共16位,2字节。
    • RGB_565:共16位,2字节,只存储RGB值。

02.Bitmap常见四种颜色格式

2.1 什么是bitmap

  • 位图文件(Bitmap),扩展名可以是.bmp或者.dib。位图是Windows标准格式图形文件,它将图像定义为由点(像素)组成,每个点可以由多种色彩表示,包括2、4、8、16、24和32位色彩。位图文件是非压缩格式的,需要占用较大存储空间。

2.2 Android常见是那种

  • 在Gesture类中

  • 在Notification类中
  • 在fw源码中bitmap图片一般是以ARGB_8888(ARGB分别代表的是透明度,红色,绿色,蓝色,每个值分别用8bit来记录,也就是一个像素会占用4byte,共32bit)来进行存储的。

2.3 常见四种颜色格式介绍

  • 四种颜色格式如下所示

  • 说明
    • 在实际应用中而言,建议使用ARGB_8888以及RGB_565。 如果你不需要透明度,选择RGB_565,可以减少一半的内存占用。
    • ARGB_8888:ARGB分别代表的是透明度,红色,绿色,蓝色,每个值分别用8bit来记录,也就是一个像素会占用4byte,共32bit.
    • ARGB_4444:ARGB的是每个值分别用4bit来记录,一个像素会占用2byte,共16bit.
    • RGB_565:R=5bit,G=6bit,B=5bit,不存在透明度,每个像素会占用2byte,共16bit
    • ALPHA_8:该像素只保存透明度,会占用1byte,共8bit.

2.4 Bitmap到底有几种颜色格式

  • 上面我说到了常见的四种,言下之意应该不止四种,那到底有几种呢?查看源码可知,具体有6种类型。查看Bitmap源码之Config配置。

  • 配置Config.HARDWARE为啥异常,看下面源码提示

03.Bitmap压缩技术

3.1 质量压缩

  • 质量压缩方法:在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的,这样适合去传递二进制的图片数据,比如分享图片,要传入二进制数据过去,限制500kb之内。

    • 1、bitmap图片的大小不会改变
    • 2、bytes.length是随着quality变小而变小的。
    /**
    * 第一种:质量压缩法
    * @param image 目标原图
    * @param maxSize 最大的图片大小
    * @return bitmap,注意可以测试以下压缩前后bitmap的大小值
    */
    public static Bitmap compressImage(Bitmap image , long maxSize) {
    int byteCount = image.getByteCount();
    Log.i("yc压缩图片","压缩前大小"+byteCount);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    // 把ByteArrayInputStream数据生成图片
    Bitmap bitmap = null;
    // 质量压缩方法,options的值是0-100,这里100表示原来图片的质量,不压缩,把压缩后的数据存放到baos中
    image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    int options = 90;
    // 循环判断如果压缩后图片是否大于maxSize,大于继续压缩
    while (baos.toByteArray().length > maxSize) {
    // 重置baos即清空baos
    baos.reset();
    // 这里压缩options%,把压缩后的数据存放到baos中
    image.compress(Bitmap.CompressFormat.JPEG, options, baos);
    // 每次都减少10,当为1的时候停止,options<10的时候,递减1
    if(options == 1){
    break;
    }else if (options <= 10) {
    options -= 1;
    } else {
    options -= 10;
    }
    }
    byte[] bytes = baos.toByteArray();
    if (bytes.length != 0) {
    // 把压缩后的数据baos存放到bytes中
    bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    int byteCount1 = bitmap.getByteCount();
    Log.i("yc压缩图片","压缩后大小"+byteCount1);
    }
    return bitmap;
    } /**
    * 第一种:质量压缩法
    *
    * @param src 源图片
    * @param maxByteSize 允许最大值字节数
    * @param recycle 是否回收
    * @return 质量压缩压缩过的图片
    */
    public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize, final boolean recycle) {
    if (src == null || src.getWidth() == 0 || src.getHeight() == 0 || maxByteSize <= 0) {
    return null;
    }
    Log.i("yc压缩图片","压缩前大小"+src.getByteCount());
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    byte[] bytes;
    if (baos.size() <= maxByteSize) {// 最好质量的不大于最大字节,则返回最佳质量
    bytes = baos.toByteArray();
    } else {
    baos.reset();
    src.compress(Bitmap.CompressFormat.JPEG, 0, baos);
    if (baos.size() >= maxByteSize) { // 最差质量不小于最大字节,则返回最差质量
    bytes = baos.toByteArray();
    } else {
    // 二分法寻找最佳质量
    int st = 0;
    int end = 100;
    int mid = 0;
    while (st < end) {
    mid = (st + end) / 2;
    baos.reset();
    src.compress(Bitmap.CompressFormat.JPEG, mid, baos);
    int len = baos.size();
    if (len == maxByteSize) {
    break;
    } else if (len > maxByteSize) {
    end = mid - 1;
    } else {
    st = mid + 1;
    }
    }
    if (end == mid - 1) {
    baos.reset();
    src.compress(Bitmap.CompressFormat.JPEG, st, baos);
    }
    bytes = baos.toByteArray();
    }
    }
    if (recycle && !src.isRecycled()){
    src.recycle();
    }
    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    Log.i("yc压缩图片","压缩后大小"+bitmap.getByteCount());
    return bitmap;
    } /**
    * 第一种:质量压缩法
    *
    * @param src 源图片
    * @param quality 质量
    * @param recycle 是否回收
    * @return 质量压缩后的图片
    */
    public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality, final boolean recycle) {
    if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
    return null;
    }
    Log.i("yc压缩图片","压缩前大小"+src.getByteCount());
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    src.compress(Bitmap.CompressFormat.JPEG, quality, baos);
    byte[] bytes = baos.toByteArray();
    if (recycle && !src.isRecycled()) {
    src.recycle();
    }
    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    Log.i("yc压缩图片","压缩后大小"+bitmap.getByteCount());
    return bitmap;
    }

3.2 采样率压缩

  • 什么是采样率压缩?

    • 设置inSampleSize的值(int类型)后,假如设为n,则宽和高都为原来的1/n,宽高都减少,内存降低。上面的代码没用过options.inJustDecodeBounds = true;因为我是固定来取样的数据,为什么这个压缩方法叫采样率压缩?是因为配合inJustDecodeBounds,先获取图片的宽、高(这个过程就是取样)。然后通过获取的宽高,动态的设置inSampleSize的值。当inJustDecodeBounds设置为true的时候, BitmapFactory通过decodeResource或者decodeFile解码图片时,将会返回空(null)的Bitmap对象,这样可以避免Bitmap的内存分配, 但是它可以返回Bitmap的宽度、高度以及MimeType。
    /**
    * 第二种:按采样大小压缩
    *
    * @param src 源图片
    * @param sampleSize 采样率大小
    * @param recycle 是否回收
    * @return 按采样率压缩后的图片
    */
    public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize, final boolean recycle) {
    if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
    return null;
    }
    Log.i("yc压缩图片","压缩前大小"+src.getByteCount());
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = sampleSize;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    byte[] bytes = baos.toByteArray();
    if (recycle && !src.isRecycled()) {
    src.recycle();
    }
    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    Log.i("yc压缩图片","压缩后大小"+bitmap.getByteCount());
    return bitmap;
    } /**
    * 第二种:按采样大小压缩
    *
    * @param src 源图片
    * @param maxWidth 最大宽度
    * @param maxHeight 最大高度
    * @param recycle 是否回收
    * @return 按采样率压缩后的图片
    */
    public static Bitmap compressBySampleSize(final Bitmap src, final int maxWidth, final int maxHeight, final boolean recycle) {
    if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
    return null;
    }
    Log.i("yc压缩图片","压缩前大小"+src.getByteCount());
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    byte[] bytes = baos.toByteArray();
    BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
    options.inJustDecodeBounds = false;
    if (recycle && !src.isRecycled()) {
    src.recycle();
    }
    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    Log.i("yc压缩图片","压缩后大小"+bitmap.getByteCount());
    return bitmap;
    } /**
    * 计算获取缩放比例inSampleSize
    */
    private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;
    if (height > reqHeight || width > reqWidth) {
    final int heightRatio = Math.round((float) height / (float) reqHeight);
    final int widthRatio = Math.round((float) width / (float) reqWidth);
    inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }
    final float totalPixels = width * height;
    final float totalReqPixelsCap = reqWidth * reqHeight * 2;
    while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
    inSampleSize++;
    }
    return inSampleSize;
    }

3.3 缩放法压缩

  • Android中使用Matrix对图像进行缩放、旋转、平移、斜切等变换的。

    • Matrix提供了一些方法来控制图片变换:Matrix调用一系列set,pre,post方法时,可视为将这些方法插入到一个队列。当然,按照队列中从头至尾的顺序调用执行。其中pre表示在队头插入一个方法,post表示在队尾插入一个方法。而set表示把当前队列清空,并且总是位于队列的最中间位置。当执行了一次set后:pre方法总是插入到set前部的队列的最前面,post方法总是插入到set后部的队列的最后面
    setTranslate(float dx,float dy):控制Matrix进行位移。
    setSkew(float kx,float ky):控制Matrix进行倾斜,kx、ky为X、Y方向上的比例。
    setSkew(float kx,float ky,float px,float py):控制Matrix以px、py为轴心进行倾斜,kx、ky为X、Y方向上的倾斜比例。
    setRotate(float degrees):控制Matrix进行depress角度的旋转,轴心为(0,0)。
    setRotate(float degrees,float px,float py):控制Matrix进行depress角度的旋转,轴心为(px,py)。
    setScale(float sx,float sy):设置Matrix进行缩放,sx、sy为X、Y方向上的缩放比例。
    setScale(float sx,float sy,float px,float py):设置Matrix以(px,py)为轴心进行缩放,sx、sy为X、Y方向上的缩放比例。
    • 缩放法压缩工具类代码
    /**
    * 第三种:按缩放压缩
    *
    * @param src 源图片
    * @param newWidth 新宽度
    * @param newHeight 新高度
    * @param recycle 是否回收
    * @return 缩放压缩后的图片
    */
    public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) {
    return scale(src, newWidth, newHeight, recycle);
    } public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
    return scale(src, scaleWidth, scaleHeight, recycle);
    } /**
    * 缩放图片
    *
    * @param src 源图片
    * @param scaleWidth 缩放宽度倍数
    * @param scaleHeight 缩放高度倍数
    * @param recycle 是否回收
    * @return 缩放后的图片
    */
    private static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
    if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
    return null;
    }
    Matrix matrix = new Matrix();
    matrix.setScale(scaleWidth, scaleHeight);
    Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
    if (recycle && !src.isRecycled()) {
    src.recycle();
    }
    return ret;
    }

04.Bitmap回收问题

4.1 recycle()方法

  • 如何调用这个recycle()方法

    if (bitmap != null && !bitmap.isRecycled()) {
    bitmap.recycle();
    bitmap = null;
    }
  • 思考以下,为何调用recycle()需要做非空判断?这里可以引出bitmap系统回收功能。小杨我如果分析不对,欢迎反馈。
    • 首先看看源码……顺便翻一下该方法的注释!我是用有道翻译的,大意如下:释放与此位图关联的本机对象,并清除对像素数据的引用。这将不会同步释放像素数据;如果没有其他引用,它只允许垃圾收集。位图被标记为“死”,这意味着如果调用getPixels()或setPixels(),它将抛出异常,并且不会绘制任何东西。此操作不能反转,因此只有在确定没有进一步使用位图的情况下才应调用该操作。这是一个高级调用,通常不需要调用,因为当没有对此位图的引用时,普通GC进程将释放此内存。
    public void recycle() {
    if (!mRecycled && mNativePtr != 0) {
    if (nativeRecycle(mNativePtr)) {
    // return value indicates whether native pixel object was actually recycled.
    // false indicates that it is still in use at the native level and these
    // objects should not be collected now. They will be collected later when the
    // Bitmap itself is collected.
    mNinePatchChunk = null;
    }
    mRecycled = true;
    }
    }
  • 通常不需要调用?这是为啥?
    • 在Android3.0以后Bitmap是存放在堆中的,只要回收堆内存即可。官方建议我们3.0以后使用recycle()方法进行回收,该方法可以不主动调用,因为垃圾回收器会自动收集不可用的Bitmap对象进行回收。
    • 那么何是进行回收呢?这里面涉及到bitmap的缓存算法,还有GC回收垃圾机制。关于GC回收机制可以看我这篇博客:https://blog.csdn.net/m0_37700275/article/details/83651039
    • 大概就是移除最少使用的缓存和使用最久的缓存,先说出结论,下来接着分析!

4.2 缓存原理

  • LruCache原理

    • LruCache是个泛型类,内部采用LinkedHashMap来实现缓存机制,它提供get方法和put方法来获取缓存和添加缓存,其最重要的方法trimToSize是用来移除最少使用的缓存和使用最久的缓存,并添加最新的缓存到队列中。

4.3 Bitmap的复用

  • Android3.0之后,并没有强调Bitmap.recycle();而是强调Bitmap的复用。

    • 使用LruCache对Bitmap进行缓存,当再次使用到这个Bitmap的时候直接获取,而不用重走编码流程。
    • Android3.0(API 11之后)引入了BitmapFactory.Options.inBitmap字段,设置此字段之后解码方法会尝试复用一张存在的Bitmap。这意味着Bitmap的内存被复用,避免了内存的回收及申请过程,显然性能表现更佳。
    • 使用这个字段有几点限制:
      • 声明可被复用的Bitmap必须设置inMutable为true;
      • Android4.4(API 19)之前只有格式为jpg、png,同等宽高(要求苛刻),inSampleSize为1的Bitmap才可以复用;
      • Android4.4(API 19)之前被复用的Bitmap的inPreferredConfig会覆盖待分配内存的Bitmap设置的inPreferredConfig;
      • Android4.4(API 19)之后被复用的Bitmap的内存必须大于需要申请内存的Bitmap的内存;
      • Android4.4(API 19)之前待加载Bitmap的Options.inSampleSize必须明确指定为1。

05.Bitmap常见操作

5.1 Bitmap的压缩方式

  • 常见压缩方法Api

    • Bitmap.compress(),质量压缩,不会对内存产生影响;
    • BitmapFactory.Options.inSampleSize,内存压缩;
  • Bitmap.compress()
    • 质量压缩,不会对内存产生影响
    • 它是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的,不会减少图片的像素。进过它压缩的图片文件大小会变小,但是解码成bitmap后占得内存是不变的。
  • BitmapFactory.Options.inSampleSize
    • 内存压缩
    • 解码图片时,设置BitmapFactory.Options类的inJustDecodeBounds属性为true,可以在Bitmap不被加载到内存的前提下,获取Bitmap的原始宽高。而设置BitmapFactory.Options的inSampleSize属性可以真实的压缩Bitmap占用的内存,加载更小内存的Bitmap。
    • 设置inSampleSize之后,Bitmap的宽、高都会缩小inSampleSize倍。例如:一张宽高为2048x1536的图片,设置inSampleSize为4之后,实际加载到内存中的图片宽高是512x384。占有的内存就是0.75M而不是12M,足足节省了15倍。
    • 备注:inSampleSize值的大小不是随便设、或者越大越好,需要根据实际情况来设置。inSampleSize比1小的话会被当做1,任何inSampleSize的值会被取接近2的幂值。

5.2 Bitmap如何复用

  • Bitmap复用的实验,代码如下所示,然后看打印的日志信息

    • 从内存地址的打印可以看出,两个对象其实是一个对象,Bitmap复用成功;
    • bitmapReuse占用的内存(4346880)正好是bitmap占用内存(1228800)的四分之一;
    • getByteCount()获取到的是当前图片应当所占内存大小,getAllocationByteCount()获取到的是被复用Bitmap真实占用内存大小。虽然bitmapReuse的内存只有4346880,但是因为是复用的bitmap的内存,因而其真实占用的内存大小是被复用的bitmap的内存大小(1228800)。这也是getAllocationByteCount()可能比getByteCount()大的原因。
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    private void initBitmap() {
    BitmapFactory.Options options = new BitmapFactory.Options();
    // 图片复用,这个属性必须设置;
    options.inMutable = true;
    // 手动设置缩放比例,使其取整数,方便计算、观察数据;
    options.inDensity = 320;
    options.inTargetDensity = 320;
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg_autumn_tree_min, options);
    // 对象内存地址;
    Log.i("ycBitmap", "bitmap = " + bitmap);
    Log.i("ycBitmap", "ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount());
    // 使用inBitmap属性,这个属性必须设置;
    options.inBitmap = bitmap; options.inDensity = 320;
    // 设置缩放宽高为原始宽高一半;
    options.inTargetDensity = 160;
    options.inMutable = true;
    Bitmap bitmapReuse = BitmapFactory.decodeResource(getResources(), R.drawable.bg_kites_min, options);
    // 复用对象的内存地址;
    Log.i("ycBitmap", "bitmapReuse = " + bitmapReuse);
    Log.i("ycBitmap", "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount());
    Log.i("ycBitmap", "bitmapReuse:ByteCount = " + bitmapReuse.getByteCount() + ":::bitmapReuse:AllocationByteCount = " + bitmapReuse.getAllocationByteCount()); //11-26 18:24:07.971 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmap = android.graphics.Bitmap@9739bff
    //11-26 18:24:07.972 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmap:ByteCount = 4346880:::bitmap:AllocationByteCount = 4346880
    //11-26 18:24:07.994 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmapReuse = android.graphics.Bitmap@9739bff
    //11-26 18:24:07.994 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmap:ByteCount = 1228800:::bitmap:AllocationByteCount = 4346880
    //11-26 18:24:07.994 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmapReuse:ByteCount = 1228800:::bitmapReuse:AllocationByteCount = 4346880
    }

5.3 Bitmap使用API获取内存

  • getByteCount()

    • getByteCount()方法是在API12加入的,代表存储Bitmap的色素需要的最少内存。API19开始getAllocationByteCount()方法代替了getByteCount()。
  • getAllocationByteCount()
    • API19之后,Bitmap加了一个Api:getAllocationByteCount();代表在内存中为Bitmap分配的内存大小。
    public final int getAllocationByteCount() {
    if (mRecycled) {
    Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap! "
    + "This is undefined behavior!");
    return 0;
    }
    return nativeGetAllocationByteCount(mNativePtr);
    }
  • 思考: getByteCount()与getAllocationByteCount()的区别?
    • 一般情况下两者是相等的;
    • 通过复用Bitmap来解码图片,如果被复用的Bitmap的内存比待分配内存的Bitmap大,那么getByteCount()表示新解码图片占用内存的大小(并非实际内存大小,实际大小是复用的那个Bitmap的大小),getAllocationByteCount()表示被复用Bitmap真实占用的内存大小(即mBuffer的长度)。
  • 在复用Bitmap的情况下,getAllocationByteCount()可能会比getByteCount()大。

5.4 该博客对应测试项目地址

关于其他内容介绍

01.关于博客汇总链接

02.关于我的博客

Bitmap优化详谈的更多相关文章

  1. android:布局、绘制、内存泄露、响应速度、listview和bitmap、线程优化以及一些优化的建议!

    1.布局优化 首先删除布局中无用的控件和层级,其次有选择地使用性能较低的viewgroup,比如布局中既可以使用RelativeLayout和LinearLayout,那我们就采用LinearLayo ...

  2. Android性能优化之Bitmap的内存优化

    1.BitmapFactory解析Bitmap的原理 BitmapFactory提供的解析Bitmap的静态工厂方法有以下五种: Bitmap decodeFile(...) Bitmap decod ...

  3. Android为TV端助力 布局、绘制、内存泄露、响应速度、listview和bitmap、线程优化以及一些优化的建议!

    1.布局优化 首先删除布局中无用的控件和层级,其次有选择地使用性能较低的viewgroup,比如布局中既可以使用RelativeLayout和LinearLayout,那我们就采用LinearLayo ...

  4. Android性能优化系列之Bitmap图片优化

    https://blog.csdn.net/u012124438/article/details/66087785 在Android开发过程中,Bitmap往往会给开发者带来一些困扰,因为对Bitma ...

  5. 性能优化-Bitmap内存管理及优化

    Bitmap作为重要Android应用之一,在很多时候如果应用不当,很容易造成内存溢出,那么这篇文章的目的就在于探讨Bitmap的有效运用及其优化 缓存介绍 当多次发送请求的时候,请求同一内容,为了使 ...

  6. Android应用性能优化

    整理自http://androidperformance.com的几篇博客 代码内存优化-Java篇 避免创建不必须的对象,虽然GC可以回收不用的对象,但为对象分配内存和回收它们同样是需要消耗资源的. ...

  7. Android——BitMap(位图)相关知识总结贴

    Android中文API(136) —— Bitmap http://www.apkbus.com/android-54644-1-1.html Android 4.0 r1 API—Bitmap(S ...

  8. Android群英传》读书笔记 (4) 第八章 Activity和Activity调用栈分析 + 第九章 系统信息与安全机制 + 第十章 性能优化

    第八章 Activity和Activity调用栈分析 1.Activity生命周期理解生命周期就是两张图:第一张图是回字型的生命周期图第二张图是金字塔型的生命周期图 注意点(1)从stopped状态重 ...

  9. 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化

    第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...

  10. 第四章 Activity和Activity调用栈分析 系统信息与安全机制 性能优化

    1.Activity生命周期理解生命周期就是两张图:第一张图是回字型的生命周期图第二张图是金字塔型的生命周期图 注意点(1)从stopped状态重新回到前台状态的时候会先调用onRestart方法,然 ...

随机推荐

  1. Python-字符串format方法指定参数

    一.字符串的format方法有几种指定参数的方式:(1)按照位置传参(默认方式),传入的参数与{}一一对应(2)关键字传参,关键字(keyword)传递是根据每个参数的名字传递参数.关键字并不用遵守位 ...

  2. NC19989 [HAOI2012]容易题(EASY)

    题目链接 题目 题目描述 为了使得大家高兴,小Q特意出个自认为的简单题(easy)来满足大家,这道简单题是描述如下: 有一个数列A已知对于所有的A[i]都是1~n的自然数,并且知道对于一些A[i]不能 ...

  3. NC16591 [NOIP2010]关押罪犯

    题目链接 题目 题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用"怨气 ...

  4. 【Unity3D】花瓣特效

    1 花瓣绘制原理 ​ 如下图是实现的花瓣特效效果,为方便描述,我们将每个红色的扁状长条称为花瓣,每个花瓣中心的绿点称为花蕊,花朵的正中心称为花心. ​ 我们在 xOz 平面上绘制花朵,假设花心为 O ...

  5. RK3588开发笔记(一):基于方案商提供的宿主机交叉编译Qt5.12.10

    前言   rk3588开发车机,方案上提供的宿主机只是编译rk sdk的版本,并未编译好Qt,那么需要自行交叉编译Qt系统.选择的Qt的版本为5.12.10.   宿主机准备   下载并打开宿主机,只 ...

  6. 【算法day3】小和、荷兰国旗、快排

    小和问题 现有数组[1,3,4,2,5] 1左边是0(小于1),所以1的小和为0 3左边是1(小于3),所以3的小和为1 4左边是1.3(均小于4),所以4的小和为1+3=4 2左边是1.3.4(只有 ...

  7. Hibernate过滤器使用窍门

    本文向大家介绍Hibernate过滤器,可能好多人还不了解Hibernate过滤器,没有关系,看完本文你肯定有不少收获,希望本文能教会你更多东西. Hibernate3新增了对某个类或者集合使用预先定 ...

  8. 浅入 ABP 系列(6):数据库配置

    浅入 ABP 系列(6):数据库配置 版权护体作者:痴者工良,微信公众号转载文章需要 <NCC开源社区>同意. 目录 浅入 ABP 系列(6):数据库配置 创建标准的 EFCore 数据库 ...

  9. 浅析图数据库 Nebula Graph 数据导入工具——Spark Writer

    从 Hadoop 说起 近年来随着大数据的兴起,分布式计算引擎层出不穷.Hadoop 是 Apache 开源组织的一个分布式计算开源框架,在很多大型网站上都已经得到了应用.Hadoop 的设计核心思想 ...

  10. ffmpeg 使用记录

    这周周末尝试把我硬盘上面的视频文件压缩了一下,但是效果并不理想.其中主要有两个原因, 视频本来就是h264的编码,再重新编码也没啥用,因为限制大小的主要是码率 ffmpeg GPU加速版的h265编码 ...