bitmap mfc 加载图片_看完这篇再摸不懂Bitmap,我跪搓衣板

作者 |  Like_Codeing 地址 |  juejin.im/post/5ec7302c518825434062f497
正文 图片在移动开发中占据中举足轻重的地位,早期的android 应用页面Ui相对简单,但随着Android系统不断的升级发展, 界面元素越来越丰富,用户对体验要求越来越高,UI小姐姐们需要设计出精致的界面元素,其中不乏很多好看的图片,但是随着手机性能提升(分辨率,cpu主频,内存等),图片质量也越来越大,拍个照动不动就3M,4M,8M, 大家都知道,android 应用在创建进程时候,会分配一个指定的内存大小,准确的说话是 google原生OS的默认值是16M,但是各个厂家的系统会对这个值进行修改,如果我们应用“毫不吝啬”将这些大图直接加载到内存中,很快内存就会耗尽,最终出现OOM异常,所以图片的处理对于一个稳定、用户体验友好的应用来说非常重要,今天我们就来聊一聊Bitmap,在开发过程中把”图片“给优化一番,保证我们项目在线上稳定、流畅运行。 Bitmap初识 Bitmap图像处理的最重要类之一,用它可以获取图像文件信息,进行图像颜色变换、剪切、旋转、缩放等操作,并可以指定格式保存图像文件。 b6746852716b1ab080390d335259d8bf.png 如图,bitmap在sdk中算是元老级的人物了,从api1中就已经有了,可见其重要性。 继承关系就不解释了,实现了Parcelable 具备在内存中传递的特性。 0283b13eb1f2c7094f1c95c517ad5072.png bitmap中有两个重要的内部类 CompressFormat 以及 Config; 下面分别介绍一下这两个类
  • CompressFormat 
 CompressFormat 是用来设置压缩方式的,是个枚举类,内部提供了三种图片压缩方式类型,
  1. JPEG :表示Bitmap采用JPEG压缩算法进行压缩,压缩后的格式可以是.jpg或者.png,是一种有损压缩方式。
  2. PNG : 表示Bitmap采用PNG压缩算法进行压缩,压缩后的格式可以是.png,是一种无损压缩方式。
  3. WEBP :表示以WebP压缩算法进行图像压缩,压缩后的格式可以是".webp",是一种有损压缩,质量相同的情况下,WebP格式图像的体积要比JPEG格式图像小40%,美中不足的是,WebP格式图像的编码时间“比JPEG格式图像长8倍”, 而且还需要注意,在官方文档中有这样的描述:As of Build.VERSION_CODES.Q, a value of 100 results in a file in the lossless WEBP format. Otherwise the file will be in the lossy WEBP format. 意为Android10之后如果quality值(压缩质量)为100的话,bitmap压缩采用无损压缩格式,其他都为有损压缩;
这里有的同志会问,这都是压缩格式啊,具体怎么操作压缩呢,Bitmap为我们提供了一个可靠的方法供开发者使用,我们来顺便看看Bitmap都有什么方法,如下: 643ffedb10a08147f9d35330e28bed67.png 第一个方法就是compress()方法, 没错就是这么就这方法,一共有三个参数
  1. format :?上面已经说明了,表示压缩格式;
  2. quality :压缩质量,取值0-100,0表示最低画质压缩,100表示最高画质压缩,对于PNG压缩格式来说,该参数可以忽略,对于WEBP格式来说,小于100为有损压缩格式,会对画质产生直接影响, 等于100时候采用的是无损压缩格式,画质是不会有改变,但是图片大小得到很好压缩;
  3. stream :将压缩后的图片写到指定的输出流中;
返回值:boolean, 返回true表示成功将bitmap压缩到输出流中,然后可以通过Bitmap.Factory从相应的输入流中解析出来bitmap信息; 3a3a1802e6b682da6c812f91c956bc25.png 从官网介绍可知, 该方法在图片压缩过程中可能消耗较长时间,建议放在子线程中操作,至于为什么大家可以看看源码, 源码中会调用一个nativeCompress 的Native 方法,也就是压缩处理是放在底层处理的;
  • Config
表示位图像素的存储格式,什么意思呢?  就是bitmap在屏幕上显示的每一像素在内存中存储的格式,会影响Bitmap真实图片的透明度以及图片质量;
  1. Bitmap.Config.ALPHA_8:颜色信息只由透明度组成,占8位;
  2. Bitmap.Config.ARGB_4444:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位;
  3. Bitmap.Config.ARGB_8888:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位,是Bitmap 默认的颜色存储格式,也是最占空间的一种配置;
  4. Bitmap.Config.RGB_565:颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位;
上面说了 android 系统默认存储位图方式是 ARGB_8888, 4个通道组成,每个通道8位,分表代表透明度和RGB颜色值, 也就是说一个位图像素占用了4个字节(1个byte8个bit位), 同理:采用 Bitmap.Config.RGB_565 存储,单像素占用内存大小仅有2byte,换句话说一张图片采用ARGB_565格式相对于默认的ARGB_8888内存将减少一半,所以通过改变bitmap像素存储方式也是图片内存优化的重要渠道,这个后面会讲到; BitmapFactory 创建位图bitmap对象途径有很多种, 包括指定文件、流, 和字节数组等; fbd323ea41913731329057e99fbc90c6.png 官方文档中提供了从字节数组、指定路径,系统Resource、二进制流等方式创建Bitmap, 当然有的方法需要一些特殊参数,例如通过字节数组方式需要指定解析的起始偏移位置,长度等,有的需要指定路径 path , 或者指定 BitmapFactory.Option配置信息 , 它也是我们图片优化的重要手段;

BitmapFactort.Options这个是什么鬼呢,  很重要!bitmap加载的配置类,想要做图片内存优化是少不了跟它打“打交道”,如下其内部属性

这里我们大概只说跟图片优化相关的几个重要属性
  • insampleSize :采样率,默认1表示无缩放,等于2表示宽高缩放2倍,总大小缩小4倍;
  • inBitmap  :被复用的bitmap;
  • inJustDecodeBound : 如果设置为true,不获取图片,不分配内存,但会返回图片的高度宽度信息;
  • inMutable :是否图片内容可变,如果Bitmap复用的话,需要设置为true;
  • inDensity :加载bitmap时候对应的像素密度(后面会讲到);
  • inTargetDensity :bitmap位图被真实渲染出的像素密度,对应终端设备的屏幕像素密度(后面会讲到);
好了,Bitmap的api我们就讲到这里,因为我们今天不是主要讲解他的用法,为了给接下来的知识做一个铺垫,简单介绍bitmap的知识点,我们接下来回归”正题“ Bitmap占用内存分析 Bitmap 用来描述一张图片的长、宽、颜色等信息。通常情况下,我们可以使用 BitmapFactory 来将某一路径下的图片解析为 Bitmap 对象。 当一张图片加载到内存后,具体需要占用多大内存呢?
  • getAllocationByteCount 
  • getByteCount
  • getRowBytes
这三个方法是什么意思呢?跟内存占用又有什么关系呢,下面我们分别解释一下这三个方法 先一下这张图 f674c0640e09a2388d8304ff7a994bb3.png 上图中 是保存在 res/drawable-mdpi 目录下的一张 1920*1200,大小为 270Kb 的图片 为什么让你看这张图呢?  因为眼睛?看累了,顺便。。。  不是的,注意上面红色框中原始图片大小和尺寸,为后面压缩设定主题;  我们分别通过 Bitmap.getAllocationByteCount() 以及 Bitmap.getByteCount()和Bitmap.getRowBytes()  方法获取 该Bitmap 的相关字节大小,比如以下代码: 114e890bda71bd606962d3c2a8357e3a.png 打印结果如下
2020-05-23 10:20:10.926 7669-7669/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 10:21:52.422 7669-7669/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1800 width:2880 allocationByteCount:20736000 byteCount:20736000 rowBytes:11520 density:240 mutable:false
大家看到 allocationByteCount = byteCount = 20736000 为什么呢?  两者又有什么差距呢?   这里我们看看官方文档怎么说的: dfc517d14bb439947bf6143b226b52d2.png 该方法在api19 之后加入的,用来返回一个存储Bitmpa像素信息的内存大小, 什么意思呢?就是为Bitmpa分配的内存大小而已, 它跟getByteCount有什么关系呢?  文档上有说明,一般情况下这两个值相等,当bitmap用来复用存储另外一个比原bitmap大小更小一点图片时候getAllocationByteCount是大于getByteCount的值,换句话说通过复用Bitmap来解码图片,如果被复用的Bitmap的内存比待分配内存的Bitmap大,那么getByteCount()表示新解码图片占用内存的大小(并非实际内存大小,实际大小是复用的那个Bitmap的大小),getAllocationByteCount()表示被复用Bitmap真实占用的内存大小。 而且在api19之后系统推荐使用getAllocationByteCount,看源码 9deb7addca5ae518555dbbd5c3947458.png 所以上面日志信息两者相等是成立,那这两者又跟getRowBytes()有什么关系呢?我们接着往下看,当我打开getByteCount的源码你就瞬间明白了 d57e14f290f392df717050156a949a7f.png getByteCunt内存大小其实就是一行像素所占据字节大小 *  Bitmap高度  我们可以验证一下: 
11520 * 1800 = 20736000
结算结果非常准确,没有任何偏差,大小类似理解一个矩形面积等于长*宽一样 ,  getRowBytes代表就是该bitmap一行像素所占据的内存大小,然后再乘以高度就是整张bitmap所占用内存;  或许有的朋友又会问,那getRowBytes大小怎么来的呢?总得给个解释吧, 刚才上面解释了,它代表了bitmap一行的像素内存,这又什么意思呢?一行像素所占用内存=bitmap宽度 * 1像素所占字节大小 ,计算如下 2880 * 4 =  11520  计算结果同样没有任何偏差,此时大家是不是似乎明白了一些什么, 我这里是根据 bitmap 内存相关api 从内到外跟大家分析内存占用, 最终得出结论 Bitmap占用内存= 宽 * 高 * 一像素所占用字节内存 ,如下 2880 * 1800 * 4 = 20736000  可能有的同志发现了,内存中bitmap图片高度、宽度跟原始图片宽高不一样,这是为什么呢? 是的,确实不一样,这里有个细节知识点,我们上面在讲Bitmap相关api时候也提到过inDensity和InTargetDensity,我这里先说出结论,然后在带大家从源码角度上找答案;  实际上 BitmapFactory 在解析图片的过程中,会根据当前设备屏幕密度和图片所在的 drawable 目录来做一个对比,根据这个对比值进行缩放操作。具体公式为如下所示: 缩放比例 scale = 当前设备屏幕密度 / 图片所在 drawable 目录对应屏幕密度  Bitmap 实际占用内存 = 宽 * scale * 高 * scale * 一像素所占用字节内存,在 Android 中,各个 drawable 目录对应的屏幕密度分别为下: 2a89d342feaaab186e4f5b8bae52464f.png 在回头看我们上面那个问题,为什么图片原始宽高跟bitmap宽高不等,从我们打印的日志可知我们设备density=1.5 densityDpi=240,而图片放在drawable-mdpi , 该bitmap的desityDpi为160 ,
bitmap 真实高= 1.5 (设备densityDpi 240/图片所在drawable的densityDpi 160 ) * 1200 = 1800
bitmap 真实宽= 1.5 (设备densityDpi 240/图片所在drawable的densityDpi 160 ) * 1920 = 2800
同样结果非常准确,也就是说明我们Bitmap内存大小除了跟我们图片宽高有关系、Bitmap.Config 以及 缩放比,而缩放比大小取决于 设备屏幕密度和图片所在drawable对应密度。 如果我们把图片放到drawable-hdpi下面,bitmap内存大小会有变化么?  是变大了还是变小了?  我是打印一下日志试一下, 然后再根据上面那个规则验证一下结果,打印如下
2020-05-23 12:01:45.358 9182-9182/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 12:01:47.018 9182-9182/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1200 width:1920 allocationByteCount:9216000 byteCount:9216000 rowBytes:7680 density:240 mutable:false
Bitmap的宽高等于原始宽高,内存大小 9216000 ,原因就是图片的drawable的densityDpi变化了,根据公式大小计算
1920 * 240/240 * 1200 * 240/240 * 4 = 9216000
9216000/20736000 = 0.44..... 把图片放到mdpi下比在hdpi内存多消耗了60% 左右, 由此可见,我们在进行图片适配时候要准备多张图片放到不同drawable目录下,一方面保证了我们图片在各设备下的显示效果一致,另一方面系统加载适合的bitmap可以节省非常多内存空间,试想一下如果我们设备是640 Dpi的呢?而我们只准备了一张图片放在mdpi或者hdpi中,那么我们这张图片会消耗多大内存呀!!!   讲了这么多,Bitmap 占用内存大小我们已经总结出来了,那我们再看看源码验证一把,前面我们讲过BitmapFactory 解析Bitmap 相关api, 如:
public static Bitmap decodeResource(Resources res, int id, Options opts) {
         validate(opts);
         Bitmap bm = null;
         InputStream is = null;try {
            final TypedValue value = new TypedValue();is = res.openRawResource(id, value);
            bm = decodeResourceStream(res, value, is, null, opts);
          } catch (Exception e) {/* do nothing. If the exception happened on open, bm will be null. If it happened on close, bm is still valid. */  
         } finally { try {if (is != null) is.close();
           } catch (IOException e) { // Ignore
      } }if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); }return bm;
}
继续跟进decodeResourceStream里面(这里就截图吧) 4ff5d49d6288a3bd0825ac719ec37174.png 如果option为空就重新new一个出来,如果TypeValue不为空取出TypeValue的density信息,TypeValue是Resource解析对应资源时候的结果封装,这里就不详细解释了,大家可以自己学习一下, 从resource里面读取到图片信息后,包括该图片所在的drawable对一个的dpi,也就是TypeValue里面的density值,如果这个值为0的话此时就会用到系统的 认 DENSITY_DEFAULT,也就是这个值  public static final int DENSITY_DEFAULT = DENSITY_MEDIUM; public static final int DENSITY_MEDIUM = 160; 而inTargetDesity 大小为设备的屏幕密度 densityDpi 有了这两个值我们就可以计算bitmap的大小了, 我们接着看 decodeStream , 最终会跟到nativeDecodeStream 中, 很明显这是个native方法,因此我们知道Bitmap的内存计算其实是放在 native层做的, 那么我直接贴出native 层处理的代码吧,
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { //初始缩放系数float scale = 1.0f;if (env->GetBooleanField(options, gOptions_scaledFieldID)) {const int density = env->GetIntField(options, gOptions_densityFieldID);const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);if (density != 0 && targetDensity != 0 && density != screenDensity) { //缩放系数是当前系数密度/图片所在文件夹对应的密度;
                scale = (float) targetDensity / density;
             }
         }//原始解码出来的Bitmap;SkBitmap decodingBitmap;if(decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode) != SkImageDecoder::kSuccess) {return nullObjectReturn("decoder->decode returned false"); }//原始解码出来的Bitmap的宽高;int scaledWidth = decodingBitmap.width();int scaledHeight = decodingBitmap.height();//要使用缩放系数进行缩放,缩放后的宽高;if(willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
             scaledWidth = int(scaledWidth * scale + 0.5f);
             scaledHeight = int(scaledHeight * scale + 0.5f);
         }//源码解释为因为历史原因;sx、sy基本等于scale。const float sx = scaledWidth / float(decodingBitmap.width());const float sy = scaledHeight / float(decodingBitmap.height());
        canvas.scale(sx, sy); canvas.drawARGB(0x00, 0x00, 0x00, 0x00);
        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);// now create the java bitmapreturn GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(), bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
 native层计算如上处理,先获取图片原始宽高,根据decodeMode计算出缩放系数, 最后对canvas进行缩放,最后将Bitmap画出来从而完成Bitmap的加载操作, 如果看到这里大家基本已经了解Bitpmap加载到内存的流程和底层缩放策略了,不要停!继续聊,关于bitmap的优化还没开始讲。。。
  • assets 中的图片大小 
我们知道,Android 中的图片不仅可以保存在 drawable 目录中,还可以保存在 assets 目录下,然后通过 AssetManager 获取图片的输入流。那这种方式加载生成的 Bitmap 是多大呢?同样是上面的 girl.png,这次将它放到 assets 目录中,使用如下代码加载: dd33c72e436ec3ead55330fab6dd5bdb.png 最终打印结果如下:
2020-05-23 14:32:33.799 11603-11603/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 14:32:35.335 11603-11603/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1200 width:1920 allocationByteCount:9216000 byteCount:9216000 rowBytes:7680 density:240 mutable:false
可以看出,加载 assets 目录中的图片,系统并不会对其进行缩放操作。 Bitmap 加载优化 上面的例子也能看出,一张 270Kb 大小的图片被加载到内存后,竟然占用了 9216000 个字节,也就是 9M 左右。因此适当时候,我们需要对需要加载的图片进行缩略优化。 修改图片加载Config 修改占用空间少的存储方式可以快速有效降低图片占用内存。比如通过 BitmapFactory.Options 的 inPreferredConfig 选项,将存储方式设置为 Bitmap.Config.RGB_565。这种存储方式一个像素占用 2 个字节,所以最终占用内存直接减半。如下: 11530bfabde43aa1c9320c55e0383ce0.png 打印日志如下:
2020-05-23 14:37:06.213 11949-11949/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 14:37:07.047 11949-11949/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1200 width:1920 allocationByteCount:4608000  // 相比9216000减少一半的内存byteCount:4608000 rowBytes:3840 density:240 mutable:false
这个结论我们在介绍Bitmap 的 Config时候已经介绍过了,这里不多说了; 另外 Options 中还有一个 inSampleSize 参数,可以实现 Bitmap 采样压缩,这个参数的含义是宽高维度上每隔 inSampleSize 个像素进行一次采集。比如以下代码: 899a3b1ac448b4f90ac19721679d1686.png 因为宽高都会进行采样,所以最终图片会被缩略 4 倍,最终打印效果如下:
2020-05-23 14:42:59.440 12182-12182/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 14:43:00.332 12182-12182/com.example.practicerecycerviewitemdecoration D/AAAAA: height:600 width:960 allocationByteCount:2304000 // 为9216000的1/4,极大降低了内存byteCount:2304000 rowBytes:3840 density:240 mutable:false
Bitmap复用 场景描述  如果在 Android 某个页面创建很多个 Bitmap,比如有两张图片 A 和 B,通过点击某一按钮需要在 ImageView 上切换显示这两张图片, 78967513f66526b8451bab03ed4d2e20.gif 可以使用以下代码实现上述效果: e5bdfec4867d6d9d7c1a994e99e8d729.png 但是在每次调用 switchImage 切换图片时,都需要通过 BitmapFactory 创建一个新的 Bitmap 对象。当方法执行完毕后,这个 Bitmap 又会被 GC 回收,这就造成不断地创建和销毁比较大的内存对象,从而导致频繁 GC(或者叫内存抖动)。像 Android App 这种面相最终用户交互的产品,如果因为频繁的 GC 造成 UI 界面卡顿,还是会影响到用户体验的。可以在 Android Studio Profiler 中查看内存情况,多次切换图片后,显示的效果如下: 6fdea872e582af1c3fd8d0023bacf7a7.png
  • 使用 Options.inBitmap 优化
 实际上经过第一次显示之后,内存中已经存在了一个 Bitmap 对象。每次切换图片只是显示的内容不一样,我们可以重复利用已经占用内存的 Bitmap 空间,这个概念上面也讲过,这里具体做法就是使用 Options.inBitmap 参数。修改如下: a0137cbe7993e326403524346a8db046.png 解释说明: 第一个红框处创建一个可以用来复用的 Bitmap 对象。第二处红框,将 options.inBitmap 赋值为之前创建的bitmap 对象,从而避免重新分配内存。重新运行代码,并查看 Profiler 中的内存情况,可以发现不管我们切换图片多少次,内存占用基本处于一个水平线状态。 d6619ab854e37af657f4f22a477bd2e3.png 我们再来看日志:
2020-05-23 15:33:46.515 21739-21739/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1200 width:1920 allocationByteCount:9216000 byteCount:9216000 rowBytes:7680 density:240 mutable:true 2020-05-23 15:34:09.031 21739-21739/com.example.practicerecycerviewitemdecoration D/AAAAA: height:322 width:640 allocationByteCount:9216000 byteCount:824320 rowBytes:2560 density:240 mutable:true
第二张图片内存明显复用了第一张Bitmap内存大小 9216000,而第二张图片byteCount大小为824320,而不等于allocationByteCoung大小,就在文章开头我们也讲解过getAllocationByteCoun和getByteCount的区别,很好的解释了这个结果; 但是这里需要注意,我们第一张Bitmap比第二张Bitmap大,如果第一张Bitmap比第二章小的话,这里就不能复用了,前面我们也是提到过的,否则会直接崩溃掉, 如下: 6526a82cff1187954c4c54e28c2cd9b3.png 我们默认在onResume里面 imageView?.setImageBitmap(bitmap) 此时这个bitmap是上图image【1】对应的Bitmap,他的内存分配上面也打印过为 824320 ,然后点击切换时候我们就复用这个Bitmap内存,将image【0】内容再填充到这个bitmap中,我们试着运行一下结果发现 7c89d117a3880688ec725fadd984c18f.png 直接给我们抛出异常了, 我们在decordResource源码中找到答案了,如下 722852eef7ab59a86d3198056b46e697.png 如果bm为空,而且开启了bitmap复用,这里就会崩掉。。。  这是因为 Bitmap 的复用有一定的限制:

在 Android 4.4 版本之前,只能重用相同大小的 Bitmap 内存区域, 4.4 之后你可以重用任何 Bitmap 的内存区域,只要这块内存比将要分配内存的 bitmap 大就可以。

那么我们需要做一下处理了,如下 5547468459d261b27ad8322d5405699c.png 第一步:先初始化一个bitmap,这个bitmap我用的是bitmap[1]中的 加载到内存后的大小为824320 ; 第二步:取第一张图片也就是image[0],实际内存大小为9216000,由于我们把inJustDecodeBound=true 此时并没有正真加载到内存中,为了获取该bitmap配置信息; 第三步:判断bitmap 能否复用, 方法如下 4bcf16972acb258b7a13e08b44272808.png 获取option中的预加载bitmap的大小,然后根据位图存储格式计算预加载的bitmap大小,最后返回比较结果, 这里默认采用ARGB_8888所以✖️4; 如果预加载的bitmap所占内存大小<=被复用bitmap大小,
option.InMutable=true;
option.InBitmap=bitmap
最后一步:再次加载bitmap并实现复用; 细心的你可能也发现了在每次加载之前,除了 inBitmap 参数之外,我还将 Options.inMutable 置为 true,这里如果不置为 true 的话,BitmapFactory 将不会重复利用 Bitmap 内存,并输出相应 warning 日志:
W/BitmapFactory: Unable to reuse an immutable bitmap as an image decoder target
Bitmap缓存  当需要在界面上同时展示一大堆图片的时候,比如 ListView、RecyclerView 等,由于用户不断地上下滑动,某个 Bitmap 可能会被短时间内加载并销毁多次。这种情况下通过使用适当的缓存,可以有效地减缓 GC 频率保证图片加载效率,提高界面的响应速度和流畅性。 最常用的缓存方式就是 LruCache,基本使用方式如下: 32cc87ac143683ea685ca42e7cf332fa.png 解释说明: 图中 指定 LruCache 的最大空间为 20M,当超过 20M 时,LruCache 会根据内部缓存策略将多余 Bitmap 移除。  图中 sizeOf () 方法指定了插入 Bitmap 时的大小,当我们向 LruCache 中插入数据时,LruCache 并不知道每一个对象会占用大多内存,因此需要我们手动指定,并且根据缓存数据的类型不同也会有不同的计算方式。  最后就是我们存取操作了。 上面就是今天的内容,讲解类Bitmap的相关基础知识点和优化,Bitmap实际问题的处理远不止这么多,像截屏长图的处理,如果不处理这张”超大图“,应用很容易就崩掉,这里需要用到分片加载, 这里不多说了,大家可自行查阅官方文档学习一下。  如果大家对这篇文章感兴趣,欢迎收藏点赞,下一篇再见! ---END--- 1c5c0b0641856ed8fd1275cf84ba225b.png 转发至朋友圈,是绝对的真爱

让我知道你在看

05e719df569d830d33cd50e2dd6bb808.gif
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值