引言
1. Android性能優(yōu)化篇之內(nèi)存優(yōu)化--內(nèi)存泄漏
2.Android性能優(yōu)化篇之內(nèi)存優(yōu)化--內(nèi)存優(yōu)化分析工具
3.Android性能優(yōu)化篇之UI渲染性能優(yōu)化
4.Android性能優(yōu)化篇之計(jì)算性能優(yōu)化
5.Android性能優(yōu)化篇之電量優(yōu)化(1)——電量消耗分析
6.Android性能優(yōu)化篇之電量優(yōu)化(2)
7.Android性能優(yōu)化篇之網(wǎng)絡(luò)優(yōu)化
8.Android性能優(yōu)化篇之Bitmap優(yōu)化
9.Android性能優(yōu)化篇之圖片壓縮優(yōu)化
10.Android性能優(yōu)化篇之多線程并發(fā)優(yōu)化
11.Android性能優(yōu)化篇之?dāng)?shù)據(jù)傳輸效率優(yōu)化
12.Android性能優(yōu)化篇之程序啟動時間性能優(yōu)化
13.Android性能優(yōu)化篇之安裝包性能優(yōu)化
14.Android性能優(yōu)化篇之服務(wù)優(yōu)化
介紹
Android中常用壓縮方法分為2種:一種是降采樣率壓縮蜒车,另外一種是質(zhì)量壓縮。
第一種:
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, o);
o.inSampleSize=自己計(jì)算
o.inJustDecodeBounds = false;
BitmapFactory.decodeFile(path, o);
第二種:
bitmap.compress(Bitmap.CompressFormat.JPEG, 20, new FileOutputStream("sdcard/result.jpg"));
相信大家都用過链瓦,但是壓縮比例很小茄螃,如果壓縮的太多,就會導(dǎo)致圖片失真拆内,但是我發(fā)發(fā)現(xiàn)IOS系統(tǒng)上的圖片只有100k,200k左右卻很清晰旋圆,它們用的什么方式來壓縮的呢?
今天我們就來使用jpeg的方式來進(jìn)行對圖片壓縮:1.編碼前準(zhǔn)備工作
(1).ndk工具包下載可以到http://www.androiddevtools.cn/ 下載解壓就行了
(2).libjpeg庫源碼下載
git clone git://git.linaro.org/people/tomgall/libjpeg-turbo/libjpeg-turbo.git -b linaro-android
(3).用ndk命令進(jìn)行編譯
ndk-build APP_ABI=armeabi-v7a,armeabi
2.編寫代碼
2.1 把動態(tài)庫和頭文件添加到我們項(xiàng)目中
2.2 編寫java層代碼
public class ImageUtil {
static {
System.loadLibrary("compressImage");
}
/**
* 使用libjpeg進(jìn)行壓縮
* @param bitmap 壓縮的圖片
* @param quality 質(zhì)量
* @param dstFile 新的圖片路徑
* @param optimize 是否使用哈夫曼算法完成壓縮(使用哈夫曼算法壓縮麸恍,壓縮率高10~25倍)
* @return 是否壓縮成功
*/
public static boolean compressImage(Bitmap bitmap,int quality,String dstFile,boolean optimize){
int ret = compressBitmap( bitmap, quality, dstFile, optimize);
return ret==1;
}
public static native int compressBitmap(Bitmap bitmap, int quality, String dstFile,boolean optimize);
2.3生成頭文件
javah -classpath . -jni github.com.androidadvanced_ndk.util.ImageUtil
2.4 編寫cmake和配置gradle
cmake:
cmake_minimum_required(VERSION 3.4.1)
set(distribution_DIR ../../../../libs )
set(SOURCE_FILES src/main/cpp/compressImage.cpp)
set(INC_DIR src/main/cpp/include)
include_directories(src/main/cpp/include)
find_library( log-lib
log )
find_library(graphics jnigraphics)
add_library( libjpeg
SHARED
IMPORTED )
set_target_properties( libjpeg
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libjpeg.so)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
add_library( compressImage
SHARED
${SOURCE_FILES} )
target_link_libraries( compressImage
libjpeg
${log-lib}
${graphics})
build.gradle
ndk{
abiFilters "armeabi-v7a" ,"armeabi"
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
2.5編寫c代碼
#include <jni.h>
#include <string>
#include <stdlib.h>
#include "github_com_androidadvanced_ndk_util_ImageUtil.h"
#include <unistd.h>
#include <setjmp.h>
#include <android/bitmap.h>
#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"imagecompress",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"imagecompress",FORMAT,##__VA_ARGS__);
#define LOGW(FORMAT,...) __android_log_print(ANDROID_LOG_WARN,"imagecompress",FORMAT,##__VA_ARGS__);
#define LOGD(FORMAT,...) __android_log_print(ANDROID_LOG_DEBUG,"imagecompress",FORMAT,##__VA_ARGS__);
typedef u_int8_t BYTE;
struct my_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr *my_error_ptr;
METHODDEF(void)
my_error_exit(j_common_ptr
cinfo) {
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message)(cinfo);
LOGW("jpeg_message_table[%d]:%s",
myerr->pub.msg_code, myerr->pub.jpeg_message_table[myerr->pub.msg_code]);
longjmp(myerr
->setjmp_buffer, 1);
};
/**
* 壓縮的數(shù)據(jù) 寬 高 壓縮質(zhì)量 存放路徑 是否使用哈夫曼算法完成壓縮
*/
int generateJPEG(BYTE *data, int w, int h, jint quality, const char *name, boolean optimize);
int generateJPEG(BYTE *data, int w, int h, int quality, const char *name, boolean optimize) {
int nComponent = 3;
struct jpeg_compress_struct jcs;
//自定義的error
struct my_error_mgr jem;
jcs.err = jpeg_std_error(&jem.pub);
jem.pub.error_exit = my_error_exit;
if (setjmp(jem.setjmp_buffer)) {
return 0;
}
//為JPEG對象分配空間并初始化
jpeg_create_compress(&jcs);
//獲取文件信息
FILE *f = fopen(name, "wb");
if (f == NULL) {
return 0;
}
//指定壓縮數(shù)據(jù)源
jpeg_stdio_dest(&jcs, f);
jcs.image_width = w;
jcs.image_height = h;
jcs.arith_code = false;
jcs.input_components = nComponent;
jcs.in_color_space = JCS_RGB;
jpeg_set_defaults(&jcs);
jcs.optimize_coding = optimize;
//為壓縮設(shè)定參數(shù)灵巧,包括圖像大小,顏色空間
jpeg_set_quality(&jcs, quality, true);
//開始壓縮
jpeg_start_compress(&jcs, true);
JSAMPROW row_point[1];
int row_stride;
row_stride = jcs.image_width * nComponent;
while (jcs.next_scanline < jcs.image_height) {
row_point[0] = &data[jcs.next_scanline * row_stride];
jpeg_write_scanlines(&jcs, row_point, 1);
}
if (jcs.optimize_coding) {
LOGI("使用了哈夫曼算法完成壓縮");
} else {
LOGI("未使用哈夫曼算法");
}
//壓縮完畢
jpeg_finish_compress(&jcs);
//釋放資源
jpeg_destroy_compress(&jcs);
fclose(f);
return 1;
}
/*
* Class: github_com_androidadvanced_ndk_util_ImageUtil
* Method: compressBitmap
* Signature: (Ljava/lang/Object;ILjava/lang/String;B)I
*/
JNIEXPORT jint JNICALL Java_github_com_androidadvanced_1ndk_util_ImageUtil_compressBitmap
(JNIEnv * env, jclass clazz, jobject bitmap, jint quality, jstring dstFile,jboolean optimize){
LOGE("%s", "===>Java_github_com_androidadvanced_1ndk_util_ImageUtil_compressBitmap");
int ret;
AndroidBitmapInfo bitmapInfo;
//像素點(diǎn)argb
BYTE *pixelsColor;
//bitmap 數(shù)據(jù)
BYTE *data;
BYTE *tmpData;
//獲取android bitmap 信息
if((ret = AndroidBitmap_getInfo(env,bitmap,&bitmapInfo)) < 0){
LOGD("AndroidBitmap_getInfo() failed error=%d", ret);
return ret;
}
//鎖定bitmap,獲取像素點(diǎn)argb抹沪,存儲到pixelsColor中
if((ret = AndroidBitmap_lockPixels(env,bitmap,(void**)&pixelsColor)) < 0){
LOGD("AndroidBitmap_lockPixels() failed error=%d", ret);
return ret;
}
BYTE r, g, b;
int color;
//獲取圖片信息
int w, h, format;
w = bitmapInfo.width;
h = bitmapInfo.height;
format = bitmapInfo.format;
//只處理 RGBA_8888
if(format != ANDROID_BITMAP_FORMAT_RGBA_8888){
LOGD("AndroidBitmapInfo format is not ANDROID_BITMAP_FORMAT_RGBA_8888 error=%d", ret);
return -1;
}
LOGD("bitmap: width=%d,height=%d,size=%d , format=%d ", w,h,w*h,bitmapInfo.format);
//分配內(nèi)存(存放bitmap rgb數(shù)據(jù))
data = (BYTE *) malloc(w * h * 3);
//保存內(nèi)存首地址
tmpData=data;
//將bitmap轉(zhuǎn)rgb
int i=0;
int j=0;
for (i = 0; i < h; ++i) {
for (j = 0; j < w; ++j){
//像素點(diǎn)
color = *((int*) pixelsColor);
//取argb值(各占8位) 0xffffffff--->0xaarrggbb
r= (color >> 16) & 0xff;
g= (color >> 8) & 0xff;
b= (color >> 0) & 0xff;
*data=b;
*(data+1)=g;
*(data+2)=r;
//data只存rgb
data+=3;
//pixelsColor中存的是argb
pixelsColor+=4;
}
}
AndroidBitmap_unlockPixels(env,bitmap);
//進(jìn)行壓縮
const char* file_path = env->GetStringUTFChars(dstFile,NULL);
//壓縮圖片
ret = generateJPEG(tmpData,w,h,quality,file_path,optimize);
//釋放內(nèi)存
free((void *) tmpData);
env->ReleaseStringUTFChars(dstFile,file_path);
//釋放java-->bitmap
jclass jBitmapClass = env->GetObjectClass(bitmap);
jmethodID jRecycleMethodId = env->GetMethodID(jBitmapClass,"recycle","()V");
env->CallVoidMethod(bitmap,jRecycleMethodId,NULL);
return ret;
}
2.6使用
//線程安全
CopyOnWriteArrayList<String> compressImageList=new CopyOnWriteArrayList<>();
//開線程池
ThreadPoolManager.ThreadPool threadPool = ThreadPoolManager.getInstance().getShortTreadPool();
for (final String imagePath : imageList) {
final String temFilePath = temDir + File.separator + new File(imagePath).getName();
threadPool.excute(new Runnable() {
@Override
public void run() {
Bitmap bitmap = ImageUtil.decodeFile(imagePath);
if(ImageUtil.compressImage(bitmap,65,temFilePath,true)){
compressImageList.add(temFilePath);
}
if(bitmap != null) {
bitmap.recycle();
}
}
});
}
3.效果
壓縮前大小:
壓縮后大锌桃蕖:
我們對比發(fā)現(xiàn),壓縮了20幾倍采够,那么圖片的清晰度呢肄方?有沒有改變,或者說改變的大不大蹬癌,又沒有失真权她?
壓縮前
壓縮后
不知道你們能不能看出區(qū)別虹茶,反正我沒發(fā)現(xiàn)有多大改變。
下載地址