您的位置:首页 > 移动开发 > Android开发

android BitmapFactory的OutOfMemoryError: bitmap ...

2014-05-22 17:15 274 查看
原文地址:/article/3555123.html网上有很多解决android加载bitmap内存溢出的方法,搜了一圈做下整理总结。项目里需求是拍摄多图之后上传,部分手机会内存溢出。常用一种解决方法:即将载入的图片缩小,这种方式以牺牲图片的质量为代价。在BitmapFactory中有一个内部类BitmapFactory.Options,其中当options.inSampleSize值>1时,根据文档:Ifsettoavalue>1,requeststhedecodertosubsampletheoriginalimage,returningasmallerimagetosavememory.(1->decodesfullsize;2->decodes1/4thsize;4->decode1/16thsize).Becauseyourarelyneedtoshow
andhavefullsizebitmapimagesonyourphone.Formanipulationssmallersizesareusuallyenough.options.inSampleSize是以2的指数的倒数被进行放缩现在问题是怎么确定inSampleSize的值?每张图片的放缩大小的比例应该是不一样的!这样的话就要运行时动态确定。在BitmapFactory.Options中提供了另一个成员inJustDecodeBounds。设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。Android提供了一种动态计算的方法,见computeSampleSize().
01
public

static
int
computeSampleSize(BitmapFactory.Optionsoptions,
02
int

minSideLength,
int
maxNumOfPixels){
03
int

initialSize=computeInitialSampleSize(options,minSideLength,
04
maxNumOfPixels);
05
06
int

roundedSize;
07
if

(initialSize<=
8
){
08
roundedSize=
1
;
09
while

(roundedSize<initialSize){
10
roundedSize<<=
1
;
11
}
12
}
else
{
13
roundedSize=(initialSize+
7
)/
8

*
8
;
14
}
15
16
return

roundedSize;
17
}
18
19
private

static
int

computeInitialSampleSize(BitmapFactory.Optionsoptions,
20
int

minSideLength,
int
maxNumOfPixels){
21
double

w=options.outWidth;
22
double

h=options.outHeight;
23
24
int

lowerBound=(maxNumOfPixels==-
1
)?
1
:
25
(
int
)Math.ceil(Math.sqrt(w*h/maxNumOfPixels));
26
int

upperBound=(minSideLength==-
1
)?
128
:
27
(
int
)Math.min(Math.floor(w/minSideLength),
28
Math.floor(h/minSideLength));
29
30
if

(upperBound<lowerBound){
31
return

lowerBound;
32
}
33
34
if

((maxNumOfPixels==-
1
)&&
35
(minSideLength==-
1
)){
36
return

1
;
37
}
else
if

(minSideLength==-
1
){
38
return

lowerBound;
39
}
else
{
40
return

upperBound;
41
}
42
}
以上只做为参考,我们只要用这函数即可,
opts.inSampleSize=computeSampleSize(opts,-
1
,
128
*
128
);
要点:1、用decodeFileDescriptor()来生成bimap比decodeFile()省内存
1
FileInputStreamis==
new
FileInputStream(path);
2
bmp=BitmapFactory.decodeFileDescriptor(is.getFD(),
null
,opts);
替换
1
Bitmapbmp=BitmapFactory.decodeFile(imageFile,opts);
2
imageView.setImageBitmap(bmp);
原因:查看BitmapFactory的源码,对比一下两者的实现,可以发现decodeFile()最终是以流的方式生成bitmapdecodeFile源码:
01
public

static
BitmapdecodeFile(StringpathName,Optionsopts){
02
Bitmapbm=
null
;
03
InputStreamstream=
null
;
04
try

{
05
stream=
new
FileInputStream(pathName);
06
bm=decodeStream(stream,
null
,opts);
07
}
catch
(Exceptione){
08
/*donothing.
09
Iftheexceptionhappenedonopen,bmwillbenull.
10
*/
11
}
finally
{
12
if

(stream!=
null
){
13
try

{
14
stream.close();
15
}
catch
(IOExceptione){
16
//donothinghere
17
}
18
}
19
}
20
return

bm;
21
}
decodeFileDescriptor的源码,可以找到native本地方法decodeFileDescriptor,通过底层生成bitmapdecodeFileDescriptor源码:
01
public

static
BitmapdecodeFileDescriptor(FileDescriptorfd,RectoutPadding,Optionsopts){
02
if

(nativeIsSeekable(fd)){
03
Bitmapbm=nativeDecodeFileDescriptor(fd,outPadding,opts);
04
if

(bm==
null

&&opts!=
null
&&opts.inBitmap!=
null
){
05
throw

new
IllegalArgumentException(
"Problemdecodingintoexistingbitmap"
);
06
}
07
return

finishDecode(bm,outPadding,opts);
08
}
else
{
09
FileInputStreamfis=
new
FileInputStream(fd);
10
try

{
11
return

decodeStream(fis,outPadding,opts);
12
}
finally
{
13
try

{
14
fis.close();
15
}
catch
(Throwablet){
/*ignore*/
}
16
}
17
}
18
}
19
20
private

static
native

BitmapnativeDecodeFileDescriptor(FileDescriptorfd,Rectpadding,Optionsopts);
2、当在android设备中载入较大图片资源时,可以创建一些临时空间,将载入的资源载入到临时空间中。
1
opts.inTempStorage=
new
byte
[
16

*
1024
];
?完整代码:
01
public

static
OutputStreamdecodeBitmap(Stringpath){
02
03
BitmapFactory.Optionsopts=
new
BitmapFactory.Options();
04
opts.inJustDecodeBounds=
true
;
//设置成了true,不占用内存,只获取bitmap宽高
05
BitmapFactory.decodeFile(path,opts);
06
opts.inSampleSize=computeSampleSize(opts,-
1
,
1024
*
800
);
07
08
opts.inJustDecodeBounds=
false
;
//这里一定要将其设置回false,因为之前我们将其设置成了true
09
opts.inPurgeable=
true
;
10
opts.inInputShareable=
true
;
11
opts.inDither=
false
;
12
opts.inPurgeable=
true
;
13
opts.inTempStorage=
new
byte
[
16

*
1024
];
14
FileInputStreamis=
null
;
15
Bitmapbmp=
null
;
16
InputStreamins=
null
;
17
ByteArrayOutputStreambaos=
null
;
18
try

{
19
is=
new
FileInputStream(path);
20
bmp=BitmapFactory.decodeFileDescriptor(is.getFD(),
null
,opts);

double
scale=getScaling(opts.outWidth*opts.outHeight,
1024
*
600
);
21
Bitmapbmp2=Bitmap.createScaledBitmap(bmp,
22
(
int
)(opts.outWidth*scale),
23
(
int
)(opts.outHeight*scale),
true
);
24
bmp.recycle();
25
baos=
new
ByteArrayOutputStream();
26
bmp2.compress(Bitmap.CompressFormat.JPEG,
100
,baos);
27
bmp2.recycle();
28
return

baos;
29
}
catch
(FileNotFoundExceptione){
30
e.printStackTrace();
31
}
catch
(IOExceptione){
32
e.printStackTrace();
33
}
finally
{
34
try

{
35
is.close();
36
ins.close();
37
baos.close();
38
}
catch
(IOExceptione){
39
e.printStackTrace();
40
}
41
System.gc();
42
}
43
return

baos;
44
}
45
46
private

static
double
getScaling(
int
src,

int
des){
47
/**
48
*目标尺寸÷原尺寸sqrt开方,得出宽高百分比
49
*/
50
double

scale=Math.sqrt((
double
)des/(
double
)src);
51
return

scale;
52
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐