在绘图分类中有几篇文章讲述了Paint(画笔)类和Canvas(画布)、Path(路径)类,它们都是与绘图息息相关的,今天我们再来介绍一个也很重要的类-Bitmap这个类。接下来就总结一下Bitmap的相关内容,如有描述错误,请指出。如果你还未了解,请先看看Android 绘图(一) Paint和Android 绘图(二) Canvas、Android 绘图(三) Path。

一. Bitmap(位图)说明。

1.  概念。

   (百度结果)位图(Bitmap),扩展名可以是.bmp或者.dib。位图是Windows标准格式图形文件,它将图像定义为由点(像素)组成,每个点可以由多种色彩表示,包括2、4、8、16、24和32位色彩。例如,一幅1024×768分辨率的32位真彩图片,其所占存储字节数为:1024×768×32/(8*1024)=3072KB。

   个人理解,Bitmap是一组数据集合,存储了图片的各种信息。

    Bitmap是Android系统中图像处理有关的重要类之一,通过Bitmap,可以获取图像的相应信息,并且也可以处理图像,例如,裁剪、旋转、缩放等操作。

二. Bitmap(位图)使用。

       在介绍Bitmap(位图)使用时,先简单说明一下BitmapFactory类,BitmapFactory类提供了好多操作Bitmap(位图)的方法。

   1. BitmapFactory类。使用BitmapFactory类可以创建Bitmap(位图)。详情请看Android 位图(一) BitmapFactory类。此处就不在详说了!

   2. 生成Bitmap(位图)或者获取Bitmap(位图)

(1). 使用BitmapDrawable:

// 读取InputStream并得到位图InputStream is=res.openRawResource(R.drawable.test);BitmapDrawable bmpDraw=new BitmapDrawable(is);Bitmap bmp=bmpDraw.getBitmap();
或者采用下面的方式:
BitmapDrawable bmpDraw=(BitmapDrawable)res.getDrawable(R.drawable.test);Bitmap bmp=bmpDraw.getBitmap();
(2). Bitmap提供了一些静态方法来创建Bitmap对象,常用的静态方法:
public static Bitmap createBitmap(Bitmap src) 以src为原图生成不可变得新图像 public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,             int dstHeight, boolean filter) 以src为原图,创建新的图像,指定新图像的高宽以及是否可变。 public static Bitmap createBitmap(int width, int height, Config config) 创建指定格式、大小的位图 public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height)以source为原图,创建新的图片,指定起始坐标以及新图像的高宽。 
(3). BitmapFactory也提供了好多静态方法来创建Bitmap对象, 详情请看 Android 位图(一) BitmapFactory类 。此处就不在详说了!

PS:(1).从资源文件中获取
方式一:
 Bitmap rawBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.test);
方式二:
 /** 通过 assest 获取       获得Drawable    bitmap **/
InputStream in = this.getAssets().open("test");
Drawable da = Drawable.createFromStream(in, null);
Bitmap mm = BitmapFactory.decodeStream(in);
(2).从SD卡中得到获取
 String filePath="/sdcard/"+"test.jpg";
 Bitmap rawBitmap1 = BitmapFactory.decodeFile(filePath, null); 

3. Bitmap(位图)回收。

   当确定不在使用Bitmap(位图)时,最好能回收。为此系统提供了相应的方法。

// 先判断是否已经回收if(bitmap != null && !bitmap.isRecycled()){         // 回收并且置为null        bitmap.recycle();         bitmap = null; }
如果是在某个Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中调用上面的方法进行回收。

三. Bitmap(位图)占用内存

   位图占用内存的大小,与三个因素有关,

        

其中,第三个因素是由Bitmap.Config控制的,我们看看系统为其提供的值,

     ALPHA_8 每一个像素存储一个单独的透明度通道。不存储颜色信息,每一个像素占用1个字节。     ARGB_4444 在API13已经舍弃了,建议使用ARGB_8888替代。每一个像素存储在2个字节。RGB三种颜色通道和Alpha通道(透明度)存储一个4位的精度(16种可能的值)。这种配置大部分情况下试用于存储透明度信息,但不需要保持到内存中。推荐使用ARGB_8888替代。注:KITKAT,任何创建的位图创建配置是使用的ARGB_8888替代的。     ARGB_8888 每一个像素存储在4个字节。RGB三种颜色通道和Alpha通道(透明度)存储8位精度(256种可能的值)。这种配置非常灵活并且有高质量的效果。它应该被使用只要有可能。     RGB_565 每一个像素存储在2个字节,并且只有RGB三种颜色通道。red占用5位精度,green占用6位精度,blue占用5位精度。这种配置使用于不要求有高色值的不透明位图。

位图占用内存的大小大约等于位图长度*位图宽度*每个像素占用的字节数。

例如一张1024*1024的像素图片,假如采用ARGB_8888格式存储,它占用的内存大小大约是=1024*1024*4=4MB。

PS:Bitmap 在内存当中占用的大小其实取决于:

(1).色彩格式,前面我们已经提到,如果是 ARGB8888 那么就是一个像素4个字节,如果是 RGB565 那就是2个字节

(2).原始文件存放的资源目录(是 hdpi 还是 xxhdpi 可不能傻傻分不清楚哈)

(3).目标屏幕的密度(所以同等条件下,红米在资源方面消耗的内存肯定是要小于三星S6的)

四. 如何减少Bitmap(位图)占用内存

4.1 Jpg 和 Png。
说到这里,肯定会有人会说,我们用 jpg 吧,jpg 格式的图片不应该比 png 小么?
这确实是个好问题,因为同样一张图片,jpg 确实比 png 会多少小一些(甚至很多),原因很简单,jpg 是一种有损压缩的图片存储格式,而 png 则是 无损压缩的图片存储格式,显而易见,jpg 会比 png 小,代价也是显而易见的。
可是,这说的是文件存储范畴的事情,它们只存在于文件系统,而非内存或者显存。说得简单一点儿,我有一个极品飞车的免安装硬盘版的压缩包放在我的磁盘里面,这个游戏是不能玩的,我需要先解压,才能玩——jpg 也好,png 也好就是个压缩包的概念,而我们讨论的内存占用则是从使用角度来讨论的。
所以,jpg 格式的图片与 png 格式的图片在内存当中不应该有什么不同。
肯定有人有意见,jpg 图片读到内存就是会小,还会给我拿出例子。当然,他说的不一定是错的。因为 jpg 的图片没有 alpha 通道!!所以读到内存的时候如果用 RGB565的格式存到内存,这下大小只有 ARGB8888的一半,能不小么。。。
不过,抛开 Android 这个平台不谈,从出图的角度来看的话,jpg 格式的图片大小也不一定比 png 的小,这要取决于图像信息的内容:
JPG 不适用于所含颜色很少、具有大块颜色相近的区域或亮度差异十分明显的较简单的图片。对于需要高保真的较复杂的图像,PNG 虽然能无损压缩,但图片文件较大。
如果仅仅是为了 Bitmap 读到内存中的大小而考虑的话,jpg 也好 png 也好,没有什么实质的差别;二者的差别主要体现在:
(1). alpha 你是否真的需要?如果需要 alpha 通道,那么没有别的选择,用 png。
(2). 你的图色值丰富还是单调?就像刚才提到的,如果色值丰富,那么用jpg,如果作为按钮的背景,请用 png。
(3). 对安装包大小的要求是否非常严格?如果你的 app 资源很少,安装包大小问题不是很凸显,看情况选择 jpg 或者 png(不过,我想现在对资源文件没有苛求的应用会很少吧。。)
(4). 目标用户的 cpu 是否强劲?jpg 的图像压缩算法比 png 耗时。这方面还是要酌情选择。
所以,使用jpg 格式的图片是有可能不会减少内存占用。

4.2 使用 inSampleSize。

在Android 位图(一) BitmapFactory类这篇文章中已经说过了,此处就不再详细说明了!

这个方法主要用在图片资源本身较大,或者适当地采样并不会影响视觉效果的条件下,这时候我们输出地目标可能相对较小,对图片分辨率、大小要求不是非常的严格。

4.3 使用矩阵
用到 Bitmap 的地方,总会见到 Matrix。这时候你会想到什么?
其实想想,Bitmap 的像素点阵,还不就是个矩阵,真是你中有我,我中有你的交情啊。那么什么时候用矩阵呢?
大图小用用采样,小图大用用矩阵。
还是用前面模糊图片的例子,我们不是采样了么?内存是小了,可是图的尺寸也小了啊,我要用 Canvas 绘制这张图可怎么办?当然是用矩阵了:

方式一:

Matrix matrix = new Matrix(); matrix.preScale(2, 2, 0f, 0f); //如果使用直接替换矩阵的话,在Nexus6 5.1.1上必须关闭硬件加速 canvas.concat(matrix); canvas.drawBitmap(bitmap, 0,0, paint);
需要注意的是,在使用搭载 5.1.1 原生系统的 Nexus6 进行测试时发现,如果使用 Canvas 的 setMatrix 方法,可能会导致与矩阵相关的元素的绘制存在问题,本例当中如果使用 setMatrix 方法,bitmap 将不会出现在屏幕上。因此请尽量使用 canvas 的 scale、rotate 这样的方法,或者使用 concat 方法。
方式二:

Matrix matrix = new Matrix(); matrix.preScale(2, 2, 0, 0); canvas.drawBitmap(bitmap, matrix, paint);
这样,绘制出来的图就是放大以后的效果了,不过占用的内存却仍然是我们采样出来的大小。
如果我要把图片放到 ImageView 当中呢?一样可以,请看:
Matrix matrix = new Matrix(); matrix.postScale(2, 2, 0, 0); imageView.setImageMatrix(matrix); imageView.setScaleType(ScaleType.MATRIX); imageView.setImageBitmap(bitmap);
4.4 合理选择Bitmap的像素格式
前文,我们已经说过 Bitmap的像素格式了。下面再总结一下:

格式    描述ALPHA_8     只有一个alpha通道ARGB_4444这个从API 13开始不建议使用,因为质量太差ARGB_8888ARGB四个通道,每个通道8bitRGB_565      每个像素占2Byte,其中红色占5bit,绿色占6bit,蓝色占5bit
这几个当中,
ALPHA8 没必要用,因为我们随便用个颜色就可以搞定的。
ARGB4444 虽然占用内存只有 ARGB8888 的一半,不过已经被官方嫌弃,失宠了。
ARGB8888 是最常用的,大家应该最熟悉了。
RGB565 看到这个,我就看到了资源优化配置无处不在,这个绿色。其实如果不需要 alpha 通道,特别是资源本身为 jpg 格式的情况下,用这个格式比较理想。

五. 性能优化。

  1.避免在Bitmap上浪费内存 。对大图片,先获取图片的大小信息,根据实际需要展示大小计算inSampleSize,最后decode;

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;            // Calculate the largest inSampleSize value that is a power of 2 and keeps both          // height and width larger than the requested height and width.          while ((halfHeight / inSampleSize) > reqHeight                  && (halfWidth / inSampleSize) > reqWidth) {              inSampleSize *= 2;          }      }        return inSampleSize;  } public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,          int reqWidth, int reqHeight) {        // First decode with inJustDecodeBounds=true to check dimensions      final BitmapFactory.Options options = new BitmapFactory.Options();      options.inJustDecodeBounds = true;      BitmapFactory.decodeResource(res, resId, options);        // Calculate inSampleSize      options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);        // Decode bitmap with inSampleSize set      options.inJustDecodeBounds = false;      return BitmapFactory.decodeResource(res, resId, options);  } 
2.缓存Bitmap。

使用内存缓存(Use a Memory Cache)-LruCache。具体的请看这篇文章, Android 缓存浅谈(一)。


参考文章:Android 开发绕不过的坑:你的 Bitmap 究竟占多大内存?

更多相关文章

  1. Android(安卓)自动编译、打包生成apk文件 2 - 使用原生Ant方式
  2. Android漫游记(1)---内存映射镜像(memory maps)
  3. Android创建和使用数据库详…
  4. android之命令行创建AVD
  5. 【Android(安卓)内存优化】Bitmap 长图加载 ( BitmapRegionDecod
  6. Android基本界面元素的使用与讲解
  7. android:maxLines和android:ellipsize同时使用导致显示异常
  8. Android(安卓)多线程之 Handler 基本使用
  9. Android高手进阶教程(七)之----Android(安卓)中Preferences的使

随机推荐

  1. Android 常用技术
  2. android 使用signingConfigs进行打包
  3. Android studio Gradle插件从3.0.0降到2.
  4. Android实现动态自动匹配输入内容功能
  5. 【Android】AlertDialog中的EditText不能
  6. Android:横屏时禁止输入法全屏
  7. Android图片圆角转换 RoundedImageView开
  8. 第八章 Activity与Activity调用栈分析
  9. Android:android生命周期(详细总结)
  10. android L nexus5 刷机