BitmapHelper

#####背景:调用相机拍照,结果返回时,导致内存迅速升高,内存不足时会导致OOM

发现的原因:在相机拍照后,会生成很大的照片,分辨率也很高,因此占用的内存会很大,在网上找了一些同学的相机拍照代码块发现,使用到一个BitmapHelper的类,本类的主要作用是:分析拍摄的照片,如果有方向不对了对照片进行旋转,进行旋转需要吧图片加载到内存中,原尺寸的照片通常会很大,会占用两个Bitmap才能够完成旋转,因此bitmap helper在旋转之前进行了尺寸的压缩。
不同的手机或者pad对尺寸进行修改会造成所放的比例计算差异。。。好多废话,下面是我对Bitmap Helper类的简单修改,能够让不同的尺寸或方向的照片进行正确的缩放。

Bitmap Helper能够将拍摄的照片缩放后保存在某个目录中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
public class BitmapHelper {

/**
* get the orientation of the bitmap {@link ExifInterface}
*
* @param path
* @return
*/
public static int getDegress(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}

/**
* rotate the bitmap
*
* @param bitmap
* @param degress
* @return
*/
public static Bitmap rotateBitmap(Bitmap bitmap, int degress) {
if (bitmap != null) {
Matrix m = new Matrix();
m.postRotate(degress);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
return bitmap;
}
return bitmap;
}

/**
* caculate the bitmap sampleSize
*
* @param
* @return
*/
public static int caculateInSampleSize(Options options, int rqsW, int rqsH) {
int height = options.outHeight;
int width = options.outWidth;
//主要修改这里,让最宽的对应设定的宽,高同理
if (width > height) {
int temp = width;
width = height;
height = temp;
}
if (rqsW > rqsH) {
int rqsT = rqsH;
rqsH = rqsW;
rqsW = rqsT;
}

int inSampleSize = 1;
if (rqsW == 0 || rqsH == 0) return 1;
if (height > rqsH || width > rqsW) {
int heightRatio = Math.round((float) height / (float) rqsH);
int widthRatio = Math.round((float) width / (float) rqsW);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
/**
* 根据需要压缩到某尺寸压缩指定路径的图片,并得到图片对象
*
* @param path
* @param rqsW
* @param rqsH
* @return
*/
public static Bitmap compressBitmap(String path, int rqsW, int rqsH) {
Options options = getBitmapOptions(path);
options.inSampleSize = caculateInSampleSize(options, rqsW, rqsH);
return BitmapFactory.decodeFile(path, options);
}

/**
* 压缩指定路径图片,并将其保存在缓存目录中,通过isDelSrc判定是否删除源文件,并获取到缓存后的图片路径
*
* @param context
* @param srcPath
* @param rqsW
* @param rqsH
* @param isDelSrc
* @return
*/
public static String compressBitmap(Context context, String srcPath, int rqsW, int rqsH, boolean isDelSrc) {
int degree = getDegress(srcPath);
Bitmap bitmap = compressBitmap(srcPath, rqsW, rqsH);//根据长宽以及图片的长宽得到缩放图片
File srcFile = new File(srcPath);
String desPath = getImageCacheDir(context) + srcFile.getName();
try {
if (degree != 0) bitmap = rotateBitmap(bitmap, degree);
File file = new File(desPath);
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(CompressFormat.JPEG, 80, fos);//80是图片质量
fos.close();
if (isDelSrc) srcFile.deleteOnExit();

} catch (Exception e) {
}

bitmap.recycle();
System.gc();

return desPath;
}

/**
* 压缩某个输入流中的图片,可以解决网络输入流压缩问题,并得到图片对象
*
* @return Bitmap {@link Bitmap}
*/
public static Bitmap compressBitmap(InputStream is, int reqsW, int reqsH) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ReadableByteChannel channel = Channels.newChannel(is);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (channel.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) baos.write(buffer.get());
buffer.clear();
}
byte[] bts = baos.toByteArray();
Bitmap bitmap = compressBitmap(bts, reqsW, reqsH);
is.close();
channel.close();
baos.close();
return bitmap;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
}
}

/**
* 压缩指定byte[]图片,并得到压缩后的图像
*
* @param bts
* @param reqsW
* @param reqsH
* @return
*/
public static Bitmap compressBitmap(byte[] bts, int reqsW, int reqsH) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bts, 0, bts.length, options);
options.inSampleSize = caculateInSampleSize(options, reqsW, reqsH);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(bts, 0, bts.length, options);


/**
* 压缩已存在的图片对象,并返回压缩后的图片
*
* @param bitmap
* @param reqsW
* @param reqsH
* @return
*/
public static Bitmap compressBitmap(Bitmap bitmap, int reqsW, int reqsH) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, 100, baos);
byte[] bts = baos.toByteArray();
Bitmap res = compressBitmap(bts, reqsW, reqsH);
baos.close();
return res;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return bitmap;
}
}

/**
* 压缩资源图片,并返回图片对象
*
* @param res {@link Resources}
* @param resID
* @param reqsW
* @param reqsH
* @return
*/
public static Bitmap compressBitmap(Resources res, int resID, int reqsW, int reqsH) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resID, options);
options.inSampleSize = caculateInSampleSize(options, reqsW, reqsH);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resID, options);
}
/**
* 得到指定路径图片的options
*
* @param srcPath
* @return Options {@link Options}
*/
public static Options getBitmapOptions(String srcPath) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(srcPath, options);
options.inJustDecodeBounds = false;
return options;
}

/**
* 获取图片缓存路径
*
* @param context
* @return
*/
private static String getImageCacheDir(Context context) {
String dir = FileUtils.getCacheDir() + "Image" + File.separator;
File file = new File(dir);
if (!file.exists()) file.mkdirs();
return dir;
}
}

###压缩的方式
上面不仅用到尺寸压缩,而且用到了图片质量压缩
bitmap.compress(CompressFormat.JPEG, 80, fos);//80是图片质量
这个就是质量压缩。
质量压缩需要把文件加载到内存中,因此大图片质量压缩也会瞬间占用大量内存,因此我使用的是先尺寸压缩,再质量压缩的方式。

####关于质量压缩
质量压缩android比IOS的质量压缩采用的据说是同一个压缩方式,但是压缩中的一个参数,android默认设置为false,IOS默认设置为true,结果是设置为相同质量压缩下,iOS文件占用的空间比android少。因此我们可以把压缩的源代码搞过来,把false改为true。
我的实际测试,质量的压缩,true的比false仅仅小了30K~100k(图片越小越不明显) 。
使用方式就是:倒入.so文件和NativeUtil

so文件下载地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class NativeUtil {
private static int DEFAULT_QUALITY = 95;

public static void compressBitmap(Bitmap bit, String fileName,
boolean optimize) {
compressBitmap(bit, DEFAULT_QUALITY, fileName, optimize);

}

public static void compressBitmap(Bitmap bit, int quality, String fileName,
boolean optimize) {
Log.d("native", "compress of native");
if (bit.getConfig() != Config.ARGB_8888) {
Bitmap result = Bitmap.createBitmap(bit.getWidth(), bit.getHeight(),
Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, bit.getWidth(), bit.getHeight());
canvas.drawBitmap(bit, null, rect, null);
saveBitmap(result, quality, fileName, optimize);
result.recycle();
} else {
saveBitmap(bit, quality, fileName, optimize);
}

}

private static void saveBitmap(Bitmap bit, int quality, String fileName,
boolean optimize) {
compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality,
fileName.getBytes(), optimize);

}

private static native String compressBitmap(Bitmap bit, int w, int h,
int quality, byte[] fileNameBytes, boolean optimize);

static {
System.loadLibrary("jpegbither");
System.loadLibrary("bitherjni");

}

}

博主github主页:Github主页 有一个仿IOS的旋转等待View,欢迎使用点星星