Android性能优化|微信图片高效传输,原来用的是——Bitmap压缩方案

一、Bitmap定义 bitmap:是使用bit位来存储数据的一种结构,当数据有明确的上下界时,我们可以转换到bitmap去存储,比如0 ~ 8区间的数,如果使用int来存,则需要耗费32字节大小,如果使用位来存,只需要花费1个字节大小,相差32倍,在大数据量的情况下,比较节约空间,而且索引效率高。
bitmap的缺点也很明显,首先,当数据比较稀疏时,bitmap显然比较浪费空间,如果要存储整个int32的数据,则需要512MB的空间大小,其次,无法对重复数据进行排序和查找。为了解决bitmap在稀疏数据集下浪费空间的问题,出现了几种改进算法,下面将结合实例来讲解。
二、Bitmap压缩方法

  1. 质量压缩:内存不变,压缩转化后的bytes.length 减少,适用于传输,png无效
  2. 采样率压缩(Options) :改变宽高,减少像素,采用一定的采样算法
  3. 缩放法压缩(Matrix) :改变宽高,减少像素,采用一定的缩放算法(数字图像处理相关)
  4. RGB_ 565:改变字节数
    Android性能优化|微信图片高效传输,原来用的是——Bitmap压缩方案
    文章图片
三、基础知识 1.1色彩模式
  • ARGB:指的是一种色彩模式,里面A代表Alpha,R表示red, G表示green,B表示blue
  • 自然界中所有的可见颜色都是由红、绿、蓝组成的,所以红、绿、蓝又称三原色,每个原色都存储着所表示颜色的信息值
  • A->alpht(透明度),R->red(红色),G->green(绿色),B-blue(蓝色)
1.2四种模式的区别 bitmap在内存中存在四种色彩的存储模式,它们的本质区别体现在每种模式下的bitmap的每个像素点,在内存中大小和组成成分的区别
1.3具体对比
  • ALPHA_8: A ->8bit->一个字节,即8bit,一个像素总共占一个字节
  • ARGB_8888: A->8bit->一个字节,R->8bit->一个字节,G->8bit->一个字节,B->8bit->一个字节,一个像素总共占用四个字节,8 + 8+ 8 + 8 = 32bit = 4byte
  • ARGB_4444: A->4bit,R->4bit,G->4bit,B->4bit,一个像素总共占用两个字节,4 + 4 + 4 + 4 = 16bit = 2byte
  • GB_565: R->5bit,G->6bit,B->5bit,一个像素总共占用两个字节,5+ 6 + 5 = 16bit = 2byte
1.4bitmap内存占用大小计算方式 一张bitmap内存占用大小 = 像素点数 * 每个像素点内存占用大小 = width * height * 每个像素点占用内存大小
tips
  • 我们知道了决定了bitmap内存占用的因素,只有改变这些因素才能改变bitmap的内存占用大小,Bitmap的压缩方案也是基于改变这些因素,来减小内存的占用
  • bitmap的内存占用大小与在本地磁盘大小是不同的概念
1.5图片存在的形式
  • 以File的形式存在于SD卡/磁盘中
  • 以Stream的形式存在于内存中
  • 以Bitmap的形式存在于内存中
1.6BitampFactory加载Bitmap对象的方式
  • decodeFile 从文件中加载Bitmap对象
  • ecodeResource从资源中加载Bitmap对象
  • decodeStream从输入流加载Bitmap对象
  • decodeByteArray 从字节数组中加载Bitmap对象
  • Android性能优化|微信图片高效传输,原来用的是——Bitmap压缩方案
    文章图片
四、质量压缩 样板代码:
val baos = ByteArrayOutputStream() // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 bmRaw.compress(Bitmap.CompressFormat.JPEG, 50, baos) val bais = ByteArrayInputStream(baos.toByteArray()) val bmScaled = BitmapFactory.decodeStream(bais, null, null) 复制代码

说明:
使用 JPEG 格式的质量压缩
bmRaw.compress(Bitmap.CompressFormat.JPEG, 50, baos) 复制代码

  • 对一张透明图片(png),内存、宽高不变,bytes.length 减少。图片会失去透明度,透明处变黑,
  • 对一张非透明图片(png、jpg),内存、宽高不变,bytes.length 减少。
使用 PNG 格式的质量压缩
bmRaw.compress(Bitmap.CompressFormat.PNG, 50, baos) 复制代码

  • 对一张透明图片(png),没有影响
  • 对一张非透明图片(png、jpg),没有影响
五、采样率 样板代码:
val options = BitmapFactory.Options() options.inSampleSize = 2 val bmScaled = BitmapFactory.decodeResource(resources, drawableId, options) // decode 的方法: BitmapFactory.decodeFile() BitmapFactory.decodeRecourse() BitmapFactory.decodeStream() BitmapFactory.decodeByteArray() 复制代码

说明:
/** * If set to a value > 1, requests the decoder to subsample the original * image, returning a smaller image to save memory. The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. For example, inSampleSize == 4 returns * an image that is 1/4 the width/height of the original, and 1/16 the * number of pixels. Any value <= 1 is treated the same as 1. Note: the * decoder uses a final value based on powers of 2, any other value will * be rounded down to the nearest power of 2. */ public int inSampleSize;

The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. 样本大小是在解码位图中对应于单个像素的任一维度上的像素个数。
也就是说,如果 inSampleSize = 2,采样后的一个像素在 x 轴上相当于之前的 2 个像素,在 y 轴上也相当于之前的 2 个像素。 即采样后的一个像素相当于之前的 2*2=4 个像素。
六、缩放法 样板代码:
val bmRaw = BitmapFactory.decodeResource(resources, drawableId, null) val matrix = Matrix() matrix.setScale(0.5f, 0.5f) val bmScaled = Bitmap.createBitmap(bmRaw, 0, 0, bmRaw.width, bmRaw.height, matrix, true) 复制代码

说明:
与采样率法类似。
七、RGB_565 样板代码:
val options = BitmapFactory.Options() options.inPreferredConfig = Bitmap.Config.RGB_565 val bmNew = BitmapFactory.decodeResource(resources, drawableId, options) 复制代码

说明:
ALPHA_8 代表8位Alpha位图,一个像素1个字节 ARGB_4444 代表16位ARGB位图,一个像素2个字节 ARGB_8888 代表32位ARGB位图,一个像素4个字节 RGB_565 代表16位RGB位图,一个像素2个字节
* If this is non-null, the decoder will try to decode into this * internal configuration. If it is null, or the request cannot be met, * the decoder will try to pick the best matching config based on the * system's screen depth, and characteristics of the original image such * as if it has per-pixel alpha (requiring a config that also does). * * Image are loaded with the {@link Bi tmap . Config#ARGB_ 888} config by * default. public Bitmap . Config inPreferredConfig = Bi tmap . Config . ARGB8888

如果 inPreferredConfig 不为 null,解码器会尝试使用此参数指定的颜色模式来对图片进行解码,如果 inPreferredConfig 为 null 或者在解码时无法满足此参数指定的颜色模式,解码器会自动根据原始图片的特征以及当前设备的屏幕位深,选取合适的颜色模式来解码,例如,如果图片中包含透明度,那么对该图片解码时使用的配置就需要支持透明度,默认会使用 ARGB_8888 来解码。
所以直接设置 RGB_565:
  • 对于一张透明图片(png),内存、宽高不变,bitmap 也不会失去透明度。
  • 对于一张非透明图片(png、jpg),宽高不变,内存减小。
copy 一遍可以减少内存,但生成的 bitmap 会失去透明度,透明处变黑。
val bmScaled = bmRaw.copy(Bitmap.Config.RGB_565, true)

八、总结 以上就是5种图片压缩的方法,这里须要强调,他们的压缩仅仅只是对android中的bitmap来讲的。若是将这些压缩后的bitmap另存为sd中,他们的内存大小并不同。
【Android性能优化|微信图片高效传输,原来用的是——Bitmap压缩方案】android手机中,图片的所占的内存大小和不少因素相关,计算起来也很麻烦。为了计算出一个图片的内存大小,能够将图片当作一个文件来间接计算,用以下的方法:《Android核心进阶技术点》一带一路。
File file = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/DCIM/Camera/test.jpg"); Log.i("wechat", "file.length()=" + file.length() / 1024);

或者
FileInputStream fis = null; try { fis = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { Log.i("wechat", "fis.available()=" + fis.available() / 1024); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }

    推荐阅读