###1.Android图片显示遇到的问题
在Android应用中加载图片,是一个需要消耗内存的一个动作。也是Android开发者在项目优化中比较头疼的一个问题。

先了解下加载图片如何消耗内存的

图片质量

1px所占空间(1byte=8位)

1024*1024图片大小

ALPHA_8只有透明度,没有颜色,那么一个像素点占8位。

1byte

1M

RGB_565 即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

2byte

2M

ARGB_8888由4个8位组成,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位

4byte

4M

ARGB_4444由4个4位组成,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位

2byte

2M

而Android系统分配给单个应用的内存空间还是很有限的大致有16M,64M,128M等。如果一个应用加载大量高质量的图片的话就会导致一种情况出现——OOM(内存溢出)。

所以为了避免这种情况出现,就要对图片进行压缩处理显示。

对图片压缩处理就是进行裁切以及压缩,但是为了保证处理后的图片尽量达到跟原图效果一致,对图片裁切,压缩的一个度是如何控制呢。

2.使用Luban压缩图片效果

Android 鲁班热更新 鲁班加载80就不动_压缩

 

Android 鲁班热更新 鲁班加载80就不动_android_02

上面两图中的上部分图片是原图,下面部分是经过Luban压缩过后的图片

效果:

第一张原图4.55m 尺寸3456x4608。 压缩过后53k 尺寸1152x1536

第二张原图1.16m 尺寸1080x1920。 压缩过后150k 尺寸360x640

我们可以发现压缩后的图片在不放大的情况下,图片效果和原图一致,Luban的处理效果特别好。

如何使用Luban压缩图片

1.在项目中的build.gradle添加依赖(需要用到RxJava1.0)

Android 鲁班热更新 鲁班加载80就不动_android_03

2.在代码中使用Luban压缩方法

1)Listerner方式。传入图片File,调用设置压缩监听setCompressListerner方法,处理压缩结果。

Android 鲁班热更新 鲁班加载80就不动_源码_04

2)Rxjava方式,传入图片File,调用asObservable方法返回一个Observable观察者对象,使用RxJava方式来处理压缩结果。

3.Luban源码分析

源码中最主要的方法就是thirdCompress(File file),即如何计算得出理想的压缩后宽高,以及图片大小。

Luban压缩算法思路

  1. 判断图片比例值,是否处于以下区间内;
  • [1, 0.5625) 即图片处于 [1:1 ~ 9:16) 比例范围内
  • [0.5625, 0.5) 即图片处于 [9:16 ~ 1:2) 比例范围内
  • [0.5, 0) 即图片处于 [1:2 ~ 1:∞) 比例范围内
  1. 判断图片最长边是否过边界值;
  • [1, 0.5625) 边界值为:1664 * n(n=1), 4990 * n(n=2), 1280 * pow(2, n-1)(n≥3)
  • [0.5625, 0.5) 边界值为:1280 * pow(2, n-1)(n≥1)
  • [0.5, 0) 边界值为:1280 * pow(2, n-1)(n≥1)
  1. 计算压缩图片实际边长值,以第2步计算结果为准,超过某个边界值则:width / pow(2, n-1),height/pow(2, n-1)
  2. 计算压缩图片的实际文件大小,以第2、3步结果为准,图片比例越大则文件越大。
    size = (newW * newH) / (width * height) * m;
  • [1, 0.5625) 则 width & height 对应 1664,4990,1280 * n(n≥3),m 对应 150,300,300;
  • [0.5625, 0.5) 则 width = 1440,height = 2560, m = 200;
  • [0.5, 0) 则 width = 1280,height = 1280 / scale,m = 500;注:scale为比例值
  1. 判断第4步的size是否过小
  • [1, 0.5625) 则最小 size 对应 60,60,100
  • [0.5625, 0.5) 则最小 size 都为 100
  • [0.5, 0) 则最小 size 都为 100
  1. 将前面求到的值压缩图片 width, height, size 传入压缩流程,压缩图片直到满足以上数值

源码:

根据算法计算压缩后的宽高以及图片文件大小,在调用压缩方法compress,传入计算后的宽高以及图片大小

Android 鲁班热更新 鲁班加载80就不动_android_05


Android 鲁班热更新 鲁班加载80就不动_压缩_06

开始进行压缩,这里分别执行了压缩图片大小compress方法,以及压缩图片质量saveImage方法

Android 鲁班热更新 鲁班加载80就不动_压缩_07

压缩图片至预期大小,配置BitmapFactory.Options中inSampleSize的值来压缩图片的宽高

Android 鲁班热更新 鲁班加载80就不动_android_08


Android 鲁班热更新 鲁班加载80就不动_android_09

图片质量压缩

Android 鲁班热更新 鲁班加载80就不动_源码_10

4.主要代码分析

清楚了第三档压缩的实现之后,再了解使用Luban压缩时用到几个方法:

get()、load(file)、putGear() 、setFileName()、setCompressListener()、launch()、asObservable()

get(Context context)

Android 鲁班热更新 鲁班加载80就不动_Android 鲁班热更新_11

Android 鲁班热更新 鲁班加载80就不动_android_12

首先调用的是get(this),传入的是一个Context对象,调用私有的构造方法传入一个File对象创建一个Luban对象;File对象生成调用的是getPhotoCacheDir方法,来指定缓存目录。

路径为:app包名/cache/luban_disk_cache/文件名

Android 鲁班热更新 鲁班加载80就不动_压缩图片_13

load(File file)

public Luban load(File file) {
    mFile = file;
    return this;
}

这个方法比较容易理解,传入的是我们图片文件。

putGear(int gear)

public Luban putGear(int gear) {
        this.gear = gear;
        return this;
}

设置压缩等级,源码中只有两种等级:FIRST_GEAR 和 THIRD_GEAR,传入其他值无效。

private static final int FIRST_GEAR = 1;
    public static final int THIRD_GEAR = 3;

setFileName(String fileName)

设置压缩后的图片名称

public Luban setFilename(String filename) {
        this.filename = filename;
        return this;
    }

setCompressListener(OnCompressListener listener)

设置压缩监听 OnCompressListener是一个接口,里面有三个方法,对应三种状态

public Luban setCompressListener(OnCompressListener listener) {
        compressListener = listener;
        return this;
}
public interface OnCompressListener {

    /**
     * Fired when the compression is started, override to handle in your own code
     * 压缩开始
     */
    void onStart();

    /**
     * Fired when a compression returns successfully, override to handle in your own code
     * 压缩成功
     */
    void onSuccess(File file);

    /**
     * Fired when a compression fails to complete, override to handle in your own code
     * 压缩失败
     */
    void onError(Throwable e);
}

lanuch()

开始压缩,方法中使用的RxJava,根据设置的压缩等级来压缩图片

Android 鲁班热更新 鲁班加载80就不动_Android 鲁班热更新_14


Android 鲁班热更新 鲁班加载80就不动_Android 鲁班热更新_15

asObservable()

使用这个方法的话,采用的Rxjava方式返回一个观察者Observable。

public Observable<File> asObservable() {
        if (gear == FIRST_GEAR)//判断压缩等级 
            return Observable.just(mFile).map(new Func1<File, File>() {
                @Override
                public File call(File file) {
                    return firstCompress(file); //调用第一档压缩方法
                }
            });
        else if (gear == THIRD_GEAR)
            return Observable.just(mFile).map(new Func1<File, File>() {
                @Override
                public File call(File file) {
                    return thirdCompress(file);// 调用第三档压缩方法
                }
            });
        else return Observable.empty();
    }