網(wǎng)絡(luò)的優(yōu)化
1.請求的緩存冈在。
Http請求是可以做緩存的空闲。
三級緩存
內(nèi)存緩存卿操、外部緩存
算法LruCache
+自定的 一周過期等等叔汁。
Sqlite緩存+加密
BitmapFactory.Options:參數(shù):
inDensity:bitmap的像素密度
inTargetDensity:bitmap最終的像素密度
DensityDpi(屏幕的像素密度) 分辨率 Density
160dpi 320x533 1
px = dp*Density
圖片存在的幾種形式:
- File
- 流的形式
- Bitmap的形式---內(nèi)存
圖片壓縮方式
1.質(zhì)量壓縮
AB
CD
壓縮后
AA
AA
原理:通過算法摳掉(同化)了圖片中的一些某個些點附近相近的像素,達到降低質(zhì)量介紹文件大小的目的乘碑。
減小了圖片質(zhì)量
** 注意:*它其實只能實現(xiàn)對file的影響挖息,對加載這個圖片出來的bitmap內(nèi)存是無法節(jié)省的,還是那么大兽肤。 因為bitmap在內(nèi)存中的大小是按照像素計算的套腹,也就是widthheight,對于質(zhì)量壓縮资铡,并不會改變圖片的真實的像素(像素大小不會變)电禀。
** 使用場景:**
將圖片壓縮后保存到本地,或者將圖片上傳到服務器笤休。根據(jù)實際需求來尖飞。
public static void compressImageToFile(Bitmap bmp,File file){
//0~100
int quality = 50;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, quality , baos );
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2.尺寸壓縮
通過減少單位尺寸的像素值,真正意義上的降低像素店雅。1020*8880
** 使用場景:** 緩存縮略圖的時候(頭像處理)
public static void compressBitmapToFileBySize(Bitmap bmp,File file){
//壓縮尺寸倍數(shù)政基,值越大,圖片的尺寸就越小
int ratio = 4;
Bitmap result = Bitmap.createBitmap(bmp.getWidth()/ratio,
bmp.getHeight()/ratio, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
RectF rect = new RectF(0, 0, bmp.getWidth()/ratio, bmp.getHeight()/ratio);
canvas.drawBitmap(bmp, null, rect , null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
result.compress(Bitmap.CompressFormat.JPEG, 100, baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
3.采樣率壓縮
4.終極壓縮(NDK層壓縮)
1.IOS拍照1M的圖片要比安卓拍照排出來的5M的圖片還要清晰闹啦。
都是在同一個環(huán)境下沮明,保存的都是JPEG。為什么窍奋?
2.圖像處理引擎
95年 JPEG
處理引擎荐健,用于最初的在PC上面處理圖片的引擎。
05年 skia
開源的引擎, 開發(fā)了一套基于JPEG處理引擎的第二次開發(fā)琳袄。便于瀏覽器的使用江场。
07年, 安卓上面用的什么引擎
挚歧?
skia引擎扛稽,閹割版
谷歌拿了skia 思考了半天做了一個決定,去掉一個編碼算法---哈夫曼算法滑负。采用定長編碼算法在张。但是解碼還是保留了哈夫曼算法。導致了圖片處理后文件變大了矮慕。
理由:當時由于CPU和內(nèi)存在手機上都非常吃緊 性能差帮匾,而哈夫曼算法非常吃CPU,被迫用了其他的算法痴鳄。
我們的優(yōu)化:
繞過安卓Bitmap API層瘟斜,來自己編碼實現(xiàn)----修復使用哈夫曼算法。
argb
一個像素點包涵四個信息:alpha,red,green螺句,blue
a b c d e
abcde acdbe bacde ……
101010100011100
a:001
b:010
c:011
d:100
e:101
用3位來表示一個字符信息虽惭,屬于定長編碼的最優(yōu)。
abcde
001 010 011 100 101
加權(quán)信息編碼
a:80%
b:10%
c:10%
d:0%
e:0%
這種情況蛇尚,編碼就可以優(yōu)化了
a:01
b:10
c:11
優(yōu)化后的abc:01 10 11
優(yōu)化前的abc:001 010 011
通過加權(quán)可以知道那個信息為0(即沒有)芽唇,那么就表示可以省略“一位”,這樣數(shù)據(jù)量信息就會減少取劫,從而使圖片變得更小匆笤。
那么如何得到每一個字母出現(xiàn)的權(quán)重?
哈夫曼編碼:需要去掃描每個信息(圖片信息--每一個像素包括ARGB)谱邪,需要大量計算炮捧,很吃CPU。
1280800像素4
如何實現(xiàn)惦银?
下載JPEG引擎使用的庫---libjpeg庫
http://www.ijg.org/
基于該引擎來做一定的開發(fā)----自己實現(xiàn)編碼咆课。
1.導入庫文件libjpegbither.so
2.導入頭文件
3.寫mk文件
Android.mk
Applicatoin.mk
4.寫代碼
C++: XX.cpp
C: XX.c
操作步驟:
1.將android的bitmap解碼,并轉(zhuǎn)換成RGB數(shù)據(jù)璧函,一個圖片信息---像素點(argb)傀蚌,把alpha去掉。
2.JPEG對象分配空間以及初始化
3.指定壓縮數(shù)據(jù)源
4.獲取文件信息
5.為壓縮設(shè)置參數(shù)蘸吓,比如圖像大小善炫、類型、顏色空間
boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */
6.開始壓縮
jpeg_start_compress()
7.壓縮結(jié)束
jpeg_finish_compress()
8.釋放資源
/*
* Copyright 2014 http://Bither.net
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.bither.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.Log;
public class NativeUtil {
private static int DEFAULT_QUALITY = 95;
/**
* @param bit bitmap對象
* @param fileName 指定保存目錄名
* @param optimize 是否采用哈弗曼表數(shù)據(jù)計算 品質(zhì)相差5-10倍
* @Description: JNI基本壓縮
*/
public static void compressBitmap(Bitmap bit, String fileName, boolean optimize) {
saveBitmap(bit, DEFAULT_QUALITY, fileName, optimize);
}
/**
* @param image bitmap對象
* @param filePath 要保存的指定目錄
* @Description: 通過JNI圖片壓縮把Bitmap保存到指定目錄
*/
public static void compressBitmap(Bitmap image, String filePath) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 質(zhì)量壓縮方法库继,這里100表示不壓縮箩艺,把壓縮后的數(shù)據(jù)存放到baos中
int options = 20;
// JNI調(diào)用保存圖片到SD卡 這個關(guān)鍵
NativeUtil.saveBitmap(image, options, filePath, true);
}
/**
* 計算縮放比
*
* @param bitWidth 當前圖片寬度
* @param bitHeight 當前圖片高度
* @return
* @Description:函數(shù)描述
*/
public static int getRatioSize(int bitWidth, int bitHeight) {
// 圖片最大分辨率
int imageHeight = 1920;
int imageWidth = 1080;
// 縮放比
int ratio = 1;
// 縮放比,由于是固定比例縮放,只用高或者寬其中一個數(shù)據(jù)進行計算即可
if (bitWidth > bitHeight && bitWidth > imageWidth) {
// 如果圖片寬度比高度大,以寬度為基準
ratio = bitWidth / imageHeight;
} else if (bitWidth < bitHeight && bitHeight > imageHeight) {
// 如果圖片高度比寬度大宪萄,以高度為基準
ratio = bitHeight / imageHeight;
}
// 最小比率為1
if (ratio <= 0)
ratio = 1;
return ratio;
}
/**
* 調(diào)用native方法
*
* @param bit
* @param quality
* @param fileName
* @param optimize
* @Description:函數(shù)描述
*/
public static void saveBitmap(Bitmap bit, int quality, String fileName, boolean optimize) {
compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality, fileName.getBytes(), optimize);
}
/**
* 調(diào)用底層 bitherlibjni.c中的方法
*
* @param bit
* @param w
* @param h
* @param quality
* @param fileNameBytes
* @param optimize
* @return
* @Description:函數(shù)描述
*/
public static native String compressBitmap(Bitmap bit, int w, int h, int quality, byte[] fileNameBytes,
boolean optimize);
/**
* 加載lib下兩個so文件
*/
static {
System.loadLibrary("jpegbither");
System.loadLibrary("bitherjni");
}
/**
* 1. 質(zhì)量壓縮
設(shè)置bitmap options屬性艺谆,降低圖片的質(zhì)量,像素不會減少
第一個參數(shù)為需要壓縮的bitmap圖片對象拜英,第二個參數(shù)為壓縮后圖片保存的位置
設(shè)置options 屬性0-100静汤,來實現(xiàn)壓縮
* @param bmp
* @param file
*/
public static void compressImageToFile(Bitmap bmp,File file) {
// 0-100 100為不壓縮
int options = 20;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把壓縮后的數(shù)據(jù)存放到baos中
bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* 2. 尺寸壓縮
通過縮放圖片像素來減少圖片占用內(nèi)存大小
* @param bmp
* @param file
*/
public static void compressBitmapToFile(Bitmap bmp, File file){
// 尺寸壓縮倍數(shù),值越大,圖片尺寸越小
int ratio = 8;
// 壓縮Bitmap到對應尺寸
Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio, Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio);
canvas.drawBitmap(bmp, null, rect, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把壓縮后的數(shù)據(jù)存放到baos中
result.compress(Bitmap.CompressFormat.JPEG, 100 ,baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 設(shè)置圖片的采樣率居凶,降低圖片像素
* @param filePath
* @param file
*/
public static void compressBitmap(String filePath, File file){
// 數(shù)值越高虫给,圖片像素越低
int inSampleSize = 8;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
// options.inJustDecodeBounds = true;//為true的時候不會真正加載圖片,而是得到圖片的寬高信息侠碧。
//采樣率
options.inSampleSize = inSampleSize;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把壓縮后的數(shù)據(jù)存放到baos中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100 ,baos);
try {
if(file.exists())
{
file.delete();
}
else {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
#include "bitherlibjni.h"
#include <string.h>
#include <android/bitmap.h>
#include <android/log.h>
#include <stdio.h>
#include <setjmp.h>
#include <math.h>
#include <stdint.h>
#include <time.h>
//統(tǒng)一編譯方式
extern "C" {
#include "jpeg/jpeglib.h"
#include "jpeg/cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
#include "jpeg/jversion.h" /* for version message */
#include "jpeg/android/config.h"
}
#define LOG_TAG "jni"
#define LOGW(...) __android_log_write(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define true 1
#define false 0
typedef uint8_t BYTE;
char *error;
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);
error=(char*)myerr->pub.jpeg_message_table[myerr->pub.msg_code];
LOGE("jpeg_message_table[%d]:%s", myerr->pub.msg_code,myerr->pub.jpeg_message_table[myerr->pub.msg_code]);
// LOGE("addon_message_table:%s", myerr->pub.addon_message_table);
// LOGE("SIZEOF:%d",myerr->pub.msg_parm.i[0]);
// LOGE("sizeof:%d",myerr->pub.msg_parm.i[1]);
longjmp(myerr->setjmp_buffer, 1);
}
int generateJPEG(BYTE* data, int w, int h, int quality,
const char* outfilename, jboolean optimize) {
//jpeg的結(jié)構(gòu)體抹估,保存的比如寬、高弄兜、位深药蜻、圖片格式等信息瓷式,相當于java的類
struct jpeg_compress_struct jcs;
//當讀完整個文件的時候就會回調(diào)my_error_exit這個退出方法。setjmp是一個系統(tǒng)級函數(shù)语泽,是一個回調(diào)贸典。
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;
}
//初始化jsc結(jié)構(gòu)體
jpeg_create_compress(&jcs);
//打開輸出文件 wb:可寫byte
FILE* f = fopen(outfilename, "wb");
if (f == NULL) {
return 0;
}
//設(shè)置結(jié)構(gòu)體的文件路徑
jpeg_stdio_dest(&jcs, f);
jcs.image_width = w;//設(shè)置寬高
jcs.image_height = h;
// if (optimize) {
// LOGI("optimize==ture");
// } else {
// LOGI("optimize==false");
// }
//看源碼注釋,設(shè)置哈夫曼編碼:/* TRUE=arithmetic coding, FALSE=Huffman */
jcs.arith_code = false;
int nComponent = 3;
/* 顏色的組成 rgb踱卵,三個 # of color components in input image */
jcs.input_components = nComponent;
//設(shè)置結(jié)構(gòu)體的顏色空間為rgb
jcs.in_color_space = JCS_RGB;
// if (nComponent == 1)
// jcs.in_color_space = JCS_GRAYSCALE;
// else
// jcs.in_color_space = JCS_RGB;
//全部設(shè)置默認參數(shù)/* Default parameter setup for compression */
jpeg_set_defaults(&jcs);
//是否采用哈弗曼表數(shù)據(jù)計算 品質(zhì)相差5-10倍
jcs.optimize_coding = optimize;
//設(shè)置質(zhì)量
jpeg_set_quality(&jcs, quality, true);
//開始壓縮瓤漏,(是否寫入全部像素)
jpeg_start_compress(&jcs, TRUE);
JSAMPROW row_pointer[1];
int row_stride;
//一行的rgb數(shù)量
row_stride = jcs.image_width * nComponent;
//一行一行遍歷
while (jcs.next_scanline < jcs.image_height) {
//得到一行的首地址
row_pointer[0] = &data[jcs.next_scanline * row_stride];
//此方法會將jcs.next_scanline加1
jpeg_write_scanlines(&jcs, row_pointer, 1);//row_pointer就是一行的首地址,1:寫入的行數(shù)
}
jpeg_finish_compress(&jcs);//結(jié)束
jpeg_destroy_compress(&jcs);//銷毀 回收內(nèi)存
fclose(f);//關(guān)閉文件
return 1;
}
/**
* byte數(shù)組轉(zhuǎn)C的字符串
*/
char* jstrinTostring(JNIEnv* env, jbyteArray barr) {
char* rtn = NULL;
jsize alen = env->GetArrayLength( barr);
jbyte* ba = env->GetByteArrayElements( barr, 0);
if (alen > 0) {
rtn = (char*) malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements( barr, ba, 0);
return rtn;
}
jstring Java_net_bither_util_NativeUtil_compressBitmap(JNIEnv* env,
jclass thiz, jobject bitmapcolor, int w, int h, int quality,
jbyteArray fileNameStr, jboolean optimize) {
BYTE *pixelscolor;
//1.將bitmap里面的所有像素信息讀取出來,并轉(zhuǎn)換成RGB數(shù)據(jù),保存到二維byte數(shù)組里面
//處理bitmap圖形信息方法1 鎖定畫布
AndroidBitmap_lockPixels(env,bitmapcolor,(void**)&pixelscolor);
//2.解析每一個像素點里面的rgb值(去掉alpha值)颊埃,保存到一維數(shù)組data里面
BYTE *data;
BYTE r,g,b;
data = (BYTE*)malloc(w*h*3);//每一個像素都有三個信息RGB
BYTE *tmpdata;
tmpdata = data;//臨時保存data的首地址
int i=0,j=0;
int color;
for (i = 0; i < h; ++i) {
for (j = 0; j < w; ++j) {
//解決掉alpha
//獲取二維數(shù)組的每一個像素信息(四個部分a/r/g/b)的首地址
color = *((int *)pixelscolor);//通過地址取值
//0~255:
// a = ((color & 0xFF000000) >> 24);
r = ((color & 0x00FF0000) >> 16);
g = ((color & 0x0000FF00) >> 8);
b = ((color & 0x000000FF));
//改值!5恪班利!----保存到data數(shù)據(jù)里面
*data = b;
*(data+1) = g;
*(data+2) = r;
data = data + 3;
//一個像素包括argb四個值,每+4就是取下一個像素點
pixelscolor += 4;
}
}
//處理bitmap圖形信息方法2 解鎖
AndroidBitmap_unlockPixels(env,bitmapcolor);
char* fileName = jstrinTostring(env,fileNameStr);
//調(diào)用libjpeg核心方法實現(xiàn)壓縮
int resultCode = generateJPEG(tmpdata,w,h,quality,fileName,optimize);
if(resultCode ==0){
jstring result = env->NewStringUTF("-1");
return result;
}
return env->NewStringUTF("1");
}