支援Android4.0以下webp的使用
最近專案中需要使用到webp,主要目的是減少網路流量(同一張圖片,webp格式能比jpg格式小約30%)。但是Android在4.0以上BitmapFactory才支援webp,今天的操作就是要使Android4.0以下的版本也能進行webp的解壓縮。
首先,需要搭建NDK開發的環境
1.下載並安裝Cygwin
按照嚮導一路向下,(使用預設的映象路徑http://www.mirrors.163.com/,這個貌似比較快)。
到達這一步:
選擇Devel,點開。選擇我們必須安裝的5個元件:binutils ,gcc(包含core和g++) ,gcc-mingw(包含core和g++) ,gdb,make。(有的時候你需要更多的元件,根據自己需要再安裝)
選擇完後,點選下一步,直到安裝完成。
執行cygwin,輸入make -v 和 gcc -v 如果能顯示版本,則表示安裝成功。
2.下載並配置NDK
NDK安裝很簡單,下載完成後把檔案解壓到指定的位置即可。
NDK環境配置:
修改Cygwin目錄/home/<username> 下的.bash_profile檔案,在檔案末尾加入如下程式碼:
ANDROID_NDK_ROOT=/cygdrive/解壓後NDK檔案的路徑 /*(例如:/cygdrive/d/android-ndk-r9b)*/
export ANDROID_NDK_ROOT
至此,準備工作已經完畢。
其次,我們需要下載並編輯webp的原始檔
下載完成後,解壓縮,提出Android.mk檔案、src資料夾和swig檔案下的libwebp.jar檔案和libwebp_java_wrap.c檔案。並將libwebp_java_wrap.c檔案移動到src資料夾下。
在工程目錄下新建名字為jni的資料夾。然後將Android.mk檔案、src資料夾移動到jni資料夾下。最後,將libwebp.jar引入到工程中。
編輯Android.mk資料夾,在include $(CLEAR_VARS)
LOCAL_SRC_FILES := \中新增:src/libwebp_java_wrap.c \
並將include $(BUILD_STATIC_LIBRARY)該為include $(BUILD_SHARED_LIBRARY)
如下:
然後在jni資料夾下建立Application.mk檔案,編輯內容如下:
# The ARMv7 is significanly faster due to the use of the hardware FPU
APP_ABI := armeabi armeabi-v7a
APP_PLATFORM := android-8
其中APP_PLATFORM設定為支援的SDK最低版本。
儲存後,我們啟動Cygwin,然後通過cd指令進入到我們工程的資料夾下,執行指令:$NDK/ndk-build。
等到編譯結束:
至此so庫已經生成(檢視libs資料夾就會發現對應的.so檔案。同時你會發現工程目錄下多了一個obj資料夾,而且還不小。放心,它只是生成so檔案的中間檔案,不會打包到apk中)。
接下來的任務是使用JNI呼叫so庫進行應用層的開發了。
應用層的開發工程一般如下:
1. 載入so庫。
static {
System.loadLibrary("webp");//loadLibrary和Android.mk中LOCAL_MODULE:= **的定義相關,我們在Android.mk中定義為webp,這裡就寫webp。
}
2.宣告與Native方法相對應的方法。
native方法是:
SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetDecoderVersion(JNIEnv *jenv,
jclass jcls) {
jint jresult = 0 ;
int result;
(void)jenv;
(void)jcls;
result = (int)WebPGetDecoderVersion();
jresult = (jint)result;
return jresult;
}
native方法命名規則為:Java_包名_類名_應用層方法名。其中包名中的點被下劃線替代。應用層中需用生命的方法為:
public static final native int WebPGetDecoderVersion();
應用層只用宣告,不用定義,但要加上native關鍵字。
3.使用應用層宣告的方法。
下面我們按照上述方法來在應用層使用我們剛生成的so庫
記得有一個libwebp.jar檔案不,這個jar已經把應用層宣告的native方法搞好了,而且幫我們封裝了一層,我們只用呼叫其方法就ok了,但是我們還是要載入so庫,因為這個它沒有幫我們實現。
我們寫兩個方法:
static {
System.loadLibrary("webp");
}
private Bitmap webpToBitmap(byte[] encoded) {
int[] width = new int[] { 0 };
int[] height = new int[] { 0 };
byte[] decoded = libwebp.WebPDecodeARGB(encoded, encoded.length, width,
height);
int[] pixels = new int[decoded.length / 4];
ByteBuffer.wrap(decoded).asIntBuffer().get(pixels);
return Bitmap.createBitmap(pixels, width[0], height[0],
Bitmap.Config.ARGB_8888);
}
public static boolean isWebp(byte[] data) {
return data != null && data.length > 12 && data[0] == 'R'
&& data[1] == 'I' && data[2] == 'F' && data[3] == 'F'
&& data[8] == 'W' && data[9] == 'E' && data[10] == 'B'
&& data[11] == 'P';
}
基於這兩個方法,我們可以對webp圖片進行解壓縮。
下面有一個demo,大家可以參考下:
參考資料: