1. 程式人生 > >Luban(魯班)--圖片壓縮使用以及原始碼解析

Luban(魯班)--圖片壓縮使用以及原始碼解析

###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壓縮圖片效果

 

上面兩圖中的上部分圖片是原圖,下面部分是經過Luban壓縮過後的圖片

效果:

第一張原圖4.55m 尺寸3456x4608。 壓縮過後53k 尺寸1152x1536

第二張原圖1.16m 尺寸1080x1920。 壓縮過後150k 尺寸360x640

我們可以發現壓縮後的圖片在不放大的情況下,圖片效果和原圖一致,Luban的處理效果特別好。

如何使用Luban壓縮圖片

1.在專案中的build.gradle新增依賴(需要用到RxJava1.0)
這裡寫圖片描述

2.在程式碼中使用Luban壓縮方法

1)Listerner方式。傳入圖片File,呼叫設定壓縮監聽setCompressListerner方法,處理壓縮結果。
這裡寫圖片描述

2)Rxjava方式,傳入圖片File,呼叫asObservable方法返回一個Observable觀察者物件,使用RxJava方式來處理壓縮結果。
這裡寫圖片描述

專案地址

3.Luban原始碼分析

原始碼中最主要的方法就是thirdCompress(File file),即如何計算得出理想的壓縮後寬高,以及圖片大小。

  1. 判斷圖片比例值,是否處於以下區間內;
    • [1, 0.5625) 即圖片處於 [1:1 ~ 9:16) 比例範圍內
    • [0.5625, 0.5) 即圖片處於 [9:16 ~ 1:2) 比例範圍內
    • [0.5, 0) 即圖片處於 [1:2 ~ 1:∞) 比例範圍內
  2. 判斷圖片最長邊是否過邊界值;
    • [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)
  3. 計算壓縮圖片實際邊長值,以第2步計算結果為準,超過某個邊界值則:width / pow(2, n-1),height/pow(2, n-1)
  4. 計算壓縮圖片的實際檔案大小,以第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為比例值
  5. 判斷第4步的size是否過小
    • [1, 0.5625) 則最小 size 對應 60,60,100
    • [0.5625, 0.5) 則最小 size 都為 100
    • [0.5, 0) 則最小 size 都為 100
  6. 將前面求到的值壓縮圖片 width, height, size 傳入壓縮流程,壓縮圖片直到滿足以上數值

原始碼:

根據演算法計算壓縮後的寬高以及圖片檔案大小,在呼叫壓縮方法compress,傳入計算後的寬高以及圖片大小

開始進行壓縮,這裡分別執行了壓縮圖片大小compress方法,以及壓縮圖片質量saveImage方法
這裡寫圖片描述

壓縮圖片至預期大小,配置BitmapFactory.Options中inSampleSize的值來壓縮圖片的寬高

圖片質量壓縮
這裡寫圖片描述

4.主要程式碼分析

清楚了第三檔壓縮的實現之後,再瞭解使用Luban壓縮時用到幾個方法:

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

get(Context context)

這裡寫圖片描述

這裡寫圖片描述

首先呼叫的是get(this),傳入的是一個Context物件,呼叫私有的構造方法傳入一個File物件建立一個Luban物件;File物件生成呼叫的是getPhotoCacheDir方法,來指定快取目錄。

路徑為:app包名/cache/luban_disk_cache/檔名

這裡寫圖片描述

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,根據設定的壓縮等級來壓縮圖片


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();
    }

Luban存在的不足

1.不能很好的支援多圖片壓縮。(出現問題:多圖壓縮出現OOM)

2.專案使用中必須新增RxJava依賴。(已在測試專案中剔除掉RxJava依賴 「連結」



最後感謝作者 鄭梓斌