目录介绍

  • 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. vue-element-admin iframes 组件 保留 iframe 操作状态

    由于没有时间去维护这个功能,这个仓库我暂停了,当前博客内容和代码只作为实现思路参考 代码贴前面,gitee地址:https://gitee.com/chkhk/vue-element-admin 可以 ...

  2. Pandas resample数据重采样

    随机抽样,是统计学中常用的一种方法,它可以帮助我们从大量的数据中快速地构建出一组数据分析模型.在 Pandas 中,如果想要对数据集进行随机抽样,需要使用 sample() 函数. sample() ...

  3. Linux操作系统下查询NVMe盘符、Slot ID和Bus ID的对应关系

    在拆卸NVMe PCIe 固态硬盘时,需要查询Linux操作系统下NVMe盘符.Slot ID和Bus ID的对应关系. 操作步骤打开操作系统命令终端.依次执行cd /sys/bus/pci/slot ...

  4. Ubuntu20.04和22.04离线安装PostgreSQL14

    今天安装 Postgresql14 遇到一个问题, 目标服务器只有内网, 内网提供标准的apt仓库, 但是因为不能连接外网, 所以没法添加第三方仓库, 这样安装pg14就成了问题. 从pg的官网看, ...

  5. java使用Timer定时器在指定时间执行程序

    下面是一个利用Timer定时器在每天指定时间执行批处理程序的例子. 有关 java.util.Timer 详细知识请参考API. 值得注意的一点是Timer是单线程顺序执行多个任务的. package ...

  6. 【Android逆向】制作Youpk脱壳机,完成对NCSearch的脱壳操作

    1. 拉去youpk 代码或镜像,自行编译构建 youpk 代码地址 https://github.com/youlor/unpacker 2. 执行 adb reboot bootloader 3. ...

  7. 运用 Argo Workflows 协调 CI/CD 流水线

    Argo Workflows 是一个开源的容器原生工作流引擎,用于协调 CI/CD 在 Kubernetes 中的运作.它以 Kubernetes 自定义资源(CRD)的形式实现,使开发人员能够创建自 ...

  8. cmake安装及报错解决办法

    安装 yum install cmake 报错 centOS8(x86_64 或 aarch64) 系统下 yum或dnf 默认安装的 cmake-3.18.2-11.el8版本,安装后无法使用,出现 ...

  9. 数据分析day01

    数据分析三剑客 numpy pandas(重点) matplotlib numpy模块 NumPy(Numerical Python)是Python语言中做科学计算的基础库.重在于数值计算,也是大部分 ...

  10. 1-Django框架简介以及基本操作

    安装 注意:安装的磁盘目录,以及后续通过Django创建目录的时候,不要出现中文,否则会出现预料之外的错误 建议:禁止套娃,即不要在A项目中创建B项目 # 如果不指定版本号,默认最新版 pip ins ...