这个就是我们平时写自定义view的时候,要写回调方法,是一样的道理,他这里就是压缩方法的回调
[](()第五步:*launch()*
点击去看到源码为
/**
- begin compress image with asynchronous
*/
public void launch() {
build().launch(context);
}
这里,我们看到他先调用了build(),我们前面讲了,他这个方法就是赋值,然后调用了launch(context)方法,我们点进去看:
/**
- start asynchronous compress thread
*/
@UiThread private void launch(final Context context) {
if (mPaths == null || mPaths.size() == 0 && mCompressListener != null) {
mCompressListener.onError(new NullPointerException(“image file cannot be null”));
}
Iterator iterator = mPaths.iterator();
while (iterator.hasNext()) {
final String path = iterator.next();
if (Checker.isImage(path)) {
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
@Override public void run() {
try {
mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_START));
File result = Checker.isNeedCompress(mLeastCompressSize, path) ?
new Engine(path, getImageCacheFile(context, Checker.checkSuffix(path))).compress() :
new File(path);
mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_SUCCESS, result));
} catch (IOException e) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_ERROR, e));
}
}
});
} else {
Log.e(TAG, "can not read the path : " + path);
}
iterator.remove();
}
}
这个方法就是最后,执行压缩的方法,前面都是初始化,我们可以看到,他这个方法是在主线程调用的,所以,我们不用考虑切换线程的问题,直接可以操作UI变化。我一步一步的讲:
-
首先,他这个是用的迭代器,循环遍历,遍历一个就移除一个
-
然后就是通过handler发消息调用
-
具体压缩代码。最重要的就是第三点,我把第三点,提到下面讲
接着上面的第三点,具体压缩
File result = Checker.isNeedCompress(mLeastCompressSize, path) ?
new Engine(path, getImageCacheFile(context, Checker.checkSuffix(path))).compress() :
new File(path);
首先,他整体是一个三目运算符,我们点isNeedCompress()方法看一下
static boolean isNeedCompress(int leastCompressSize, String path) {
if (leastCompressSize > 0) {
File source = new File(path);
if (!source.exists()) {
return false;
}
if (source.length() <= (leastCompressSize << 10)) {
return false;
}
}
return true;
}
这个方法就是用来判断,你给定路径的图片大小和你规定的忽略文件大小比较,他这里先做了你给定的最小值判断,要大于0,不大于0就返回ture。然后做了文件是否存在的判断,如果文件不存在,就返回fals。最后,给定文件大小是不是小于等于最小值左移10位的值,小于就返回false。
然后,如果返回的是true,就去压缩,如果,返回的是false,就直接返回file文件。压缩的方法点进去:
Engine(String srcImg, File tagImg) throws IOException {
if (Checker.isJPG(srcImg)) {
this.srcExif = new ExifInterface(srcImg);
}
this.tagImg = tagImg;
this.srcImg = srcImg;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = 1;
BitmapFactory.decodeFile(srcImg, options);
this.srcWidth = options.outWidth;
this.srcHeight = options.outHeight;
}
这就又要说道另一个类了Engine类,它的类注释就是:用于操作,开始压缩,管理活动,缓存资源的类。他这里传原文件,也就是你需要压缩的图片,还有一个就是目标文件,也就是你压缩之后,要保存的文件。
我们先看第二个参数是什么怎么传的,有的人看不懂
/**
-
Returns a mFile with a cache audio name in the private cache directory.
-
@param context
-
A context.
*/
private File getImageCacheFile(Context context, String suffix) {
if (TextUtils.isEmpty(mTargetDir)) {
mTargetDir = getImageCacheDir(context).getAbsolutePath();
}
String cacheBuilder = mTargetDir + “/” +
System.currentTimeMillis() +
(int) (Math.random() * 1000) +
(TextUtils.isEmpty(suffix) ? “.jpg” : suffix);
return new File(cacheBuilder);
}
他这里就是新建一个文件,设置路径,设置名称,然后返回文件
再掉回去看Engine的构造方法,我们这里获取到了源文件和目标文件,我们只用把压缩后的流存到目标文件就行了。我之前写过一篇关于图片压缩的博客。它这里的option就是设置压缩的参数,不懂的可以看一下我之前的博客,或者用googl 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源 e百度一下就知道了。具体压缩就是用的bitmap的工厂类,调用的decodeFile方法。没错就是这一句 *BitmapFactory.decodeFile(srcImg, options);*
最后,辣么一切都准备就绪了,怎么样开始压缩呢?*compress()*
File compress() throws IOException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = computeSize();
Bitmap tagBitmap = BitmapFactory.decodeFile(srcImg, options);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
tagBitmap = rotatingImage(tagBitmap);
tagBitmap.compress(Bitmap.CompressFormat.JPEG, 60, stream);
tagBitmap.recycle();
FileOutputStream fos = new FileOutputStream(tagImg);
fos.write(stream.toByteArray());
fos.flush();
fos.close();
stream.close();
return tagImg;
}
这里面就是常规的压缩,存储的逻辑了,最最重要的压缩算法呢?就是这里的***computeSize()***方法
private int computeSize() {
srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;
int longSide = Math.max(srcWidth, srcHeight);
int shortSide = Math.min(srcWidth, srcHeight);
float scale = ((float) shortSide / longSide);
if (scale <= 1 && scale > 0.5625) {
if (longSide < 1664) {
return 1;
} else if (longSide >= 1664 && longSide < 4990) {
return 2;
} else if (longSide > 4990 && longSide < 10240) {
return 4;
} else {
return longSide / 1280 == 0 ? 1 : longSide / 1280;
}
} else if (scale <= 0.5625 && scale > 0.5) {
return longSide / 1280 == 0 ? 1 : longSide / 1280;
} else {
return (int) Math.ceil(longSide / (1280.0 / scale));
}
}123456789101112131415161718192021222324
private Bitmap rotatingImage(Bitmap bitmap) {
if (srcExif == null) return bitmap;
Matrix matrix = new Matrix();
int angle = 0;
int orientation = srcExif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
angle = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
angle = 180;
break;
case ExifInterface.ORIENTATION Android开源项目《ali1024.coding.net/public/P7/Android/git》 _ROTATE_270:
angle = 270;
break;
}
matrix.postRotate(angle);
总结:
各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。
-
BAT大厂面试题、独家面试工具包,
-
资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter
nterface.ORIENTATION Android开源项目《ali1024.coding.net/public/P7/Android/git》 _ROTATE_270:
angle = 270;
break;
}
matrix.postRotate(angle);
总结:
各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。
-
BAT大厂面试题、独家面试工具包,
-
资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter
[外链图片转存中…(img-iR27Bz1d-1650170533288)]