Android 高级工程师进阶 ~ 网易云课堂 学习笔记
【直播主题】
【直播大纲】
1、浅谈Luban图片压缩框架,开启造轮子之路
2、详解架构思路和像素压缩、质量压缩的核心原理
3、项目中的复杂业务该如何处理
戳此链接占座:https://url.163.com/rKN
Android 黑科技:图片终极压缩
支持自定义配置、不 失真和批量处理
能否直接上传原图。让后台处理?
图片服务器的磁盘空间非常昂贵
尽可能避免Android OOM异常
后台约定的规则,比如 每张图片必须<=300Kb
1,递归每张图片
2,设置图片格式
3,质量压缩
4,像素修复
5,返回压缩结果集
6,完成压缩
Android 目前常用的图片格式有 png jpeg 和 webp Bitmap.CompressFormat.JPEG
根据width * height 一个像素的所 占用的字节数 计算,宽高不变 bitmap.compress(format,quality,baos)
由于png 是无损压缩,所以设置 quality无效(不适合作为缩略图)
缩小图片分辨率,减少所占用磁盘空间和内存大小 BitmapFactory.Options.inSampleSize
减少图片的像素,降低所占用磁盘空间大小和内存大小 canvas.drawBitmap(bitmap,null,rectF,null)
Android 的图片引擎使用的是阉割版的skia 引擎,去掉了图片压缩中的哈夫曼算法
https://github.com/Curzibn/Luban
https://github.com/zetbaitsu/Compressor
1,当没有设定压缩路径时,抛异常无闪退
2,源码中,压缩比率固定住60,无法修改
3,压缩配置,参数不太适应真实项目需求
4,不能指定压缩大小,比如100kb 以内
/**
* 压缩文件到Bitmap
*
* @param filePath 图片文件地址
* @param quality 要压缩的质量
* @return
*/
public static Bitmap getSmallBitmap(String filePath, int quality) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
//计算采样,现在主流手机比较多是800*480 分辨率
options.inSampleSize = calculateInSampleSize(options, 480, 800);
//用样本大小集解码位图
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
if (bitmap == null) {
return null;
}
//读取图片角度
int degree = readPitcureDegree(filePath);
//旋转位图
bitmap = rotateBitmap(bitmap, degree);
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
} finally {
try {
if (baos != null) {
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bitmap;
}
private static Bitmap rotateBitmap(Bitmap bitmap, int rotate) {
if (bitmap == null)
return null;
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate(rotate);
return Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
}
private static int readPitcureDegree(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
return degree;
}
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 ? widthRatio : heightRatio;
}
return inSampleSize;
}
/**
* 按大小压缩
*
* @return
*/
private Bitmap getImage(String srcpath) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//开始读入图片,此时 把 options.inJustDecodeBounds 设回 true了
newOpts.inJustDecodeBounds = true;
Bitmap bitmap;
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
//现在主流手机比较多是800*480 分辨率 所以高和宽我们设置为
float hh = 800f;//这里设置高度为800f
float ww = 480f;// 这里设置宽度为480f
//缩放比,由于是固定比例缩放,只用高或宽其中一个数据进行计算即可
int be = 1;// 表示不进行缩放
if (w > h && w > ww) {//如果宽度大的话 根据宽度大小进行压缩
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {// 如果高度大的话 根据高度大小进行压缩
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0) {
be = 1;
}
newOpts.inSampleSize = be;
// 重新载入图片 ,注意此时已经把options.inJustDecodeBounds 设回 false 了
bitmap = BitmapFactory.decodeFile(srcpath, newOpts);
return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
}
/**
* 质量压缩
*
* @param image
* @return
*/
private Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//质量压缩方法,这里100 表示不压缩 把压缩后的数据存放到 baos 中
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
while (baos.toByteArray().length / 1024 > 100) {//循环判断 如果压缩图片师范大于100kb,大于继续压缩
baos.reset();//重置 baos 即清空 baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos); //这里压缩 options 把压缩后的数据存放到 baos 中
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据存放到 ByteArrayInputStream
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream 数据生成图片
return bitmap;
}
https://github.com/yoyo0316/StudyDemo/commit/8c22a4ff4eebd0142e9c2a604bed94003bf2e974