转android123 预防Android内存泄露
对于很多处理图形相关的Android开发者来说,大的Bitmap对象可能直接导致软件崩溃,Android平台如何防止内存泄露呢? 目前来说Android设备的RAM可能差距比较大,很多低端配置的256MB RAM或512MB RAM由于运行了太多的后台任务或HTC Sense这样的主题导致了处理一些高像素的图片,比如500w或800w像素的照片很容易崩溃。
1. 判断目标设备Dalvik VM内存情况
通过 java.lang.Runtime类的 long freeMemory() 方法可以获取当前进程的RAM可用情况,Runtime类需要 getRuntime() 方法来实例化。
比如获取最大可用RAM 为 Runtime.getRuntime().maxMemory();
2. Bitmap对象在打开时可以考虑先缩小图片
通过减少工作区域可以有效的降低RAM使用,由于在内存中是DIB方式,可以想象ARGB的图像占用内存为 4*height*width,比如500万像素的图片,占用内存就是500x4=2000万字节就是19MB左右。同时Java VM的异常处理机制和绘图方法可能在内部产生副本,最终消耗的运行内存是十分庞大的,对于图片打开时就进行缩小可以使用 android.graphics.BitmapFactory的相关方法来处理,这里参考Android123早期文章,Android缩略图类源代码 即可
3. 及时的显示执行Bitmap的recycle方法,以及是当时可以调用Runtime的gc方法,提示虚拟机尽快释放掉内存
Android 2.2开始新增的缩略图类ThumbnailUtils的主要方法是静态的,对于Android 2.2或API Level8以下的工程可以直接使用,本类相对于我们常规的缩略图类考虑更周全,除了尺寸比例优化外,针对OOM的内存管理方面有更周全的处理方式,完整代码如下,使用方法和介绍请查看 ThumbnailUtils - Android2.2新增类 一文
publicclassThumbnailUtils{
private static final String TAG = "ThumbnailUtils";/* Maximum pixels size for created bitmap. */
privatestaticfinalintMAX_NUM_PIXELS_THUMBNAIL=512*384;
privatestaticfinalintMAX_NUM_PIXELS_MICRO_THUMBNAIL=128*128;
private static final int UNCONSTRAINED = -1;/* Options used internally. */
privatestaticfinalintOPTIONS_NONE=0x0;
private static final int OPTIONS_SCALE_UP = 0x1;/**
*Constantusedtoindicateweshouldrecycletheinputin
*{@link#extractThumbnail(Bitmap,int,int,int)}unlesstheoutputistheinput.
*/
public static final int OPTIONS_RECYCLE_INPUT = 0x2;/**
*Constantusedtoindicatethedimensionofminithumbnail.
*@hideOnlyusedbymediaframeworkandmediaproviderinternally.
*/
public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;/**
*Constantusedtoindicatethedimensionofmicrothumbnail.
*@hideOnlyusedbymediaframeworkandmediaproviderinternally.
*/
public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;/**
*ThismethodfirstexaminesifthethumbnailembeddedinEXIFisbiggerthanourtarget
*size.Ifnot,thenit'llcreateathumbnailfromoriginalimage.Duetoefficiency
*consideration,wewanttoletMediaThumbRequestavoidcallingthismethodtwicefor
*bothkinds,soitonlyrequestsforMICRO_KINDandsetsaveImagetotrue.
*
*Thismethodalwaysreturnsa"squarethumbnail"forMICRO_KINDthumbnail.
*
*@paramfilePaththepathofimagefile
*@paramkindcouldbeMINI_KINDorMICRO_KIND
*@returnBitmap
*
*@hideThismethodisonlyusedbymediaframeworkandmediaproviderinternally.
*/
publicstaticBitmapcreateImageThumbnail(StringfilePath,intkind){
booleanwantMini=(kind==Images.Thumbnails.MINI_KIND);
inttargetSize=wantMini
?TARGET_SIZE_MINI_THUMBNAIL
:TARGET_SIZE_MICRO_THUMBNAIL;
intmaxPixels=wantMini
?MAX_NUM_PIXELS_THUMBNAIL
:MAX_NUM_PIXELS_MICRO_THUMBNAIL;
SizedThumbnailBitmapsizedThumbnailBitmap=newSizedThumbnailBitmap();
Bitmapbitmap=null;
MediaFileTypefileType=MediaFile.getFileType(filePath);
if(fileType!=null&&fileType.fileType==MediaFile.FILE_TYPE_JPEG){
createThumbnailFromEXIF(filePath,targetSize,maxPixels,sizedThumbnailBitmap);
bitmap=sizedThumbnailBitmap.mBitmap;
}if (bitmap == null) {
try{
FileDescriptorfd=newFileInputStream(filePath).getFD();
BitmapFactory.Optionsoptions=newBitmapFactory.Options();
options.inSampleSize=1;
options.inJustDecodeBounds=true;
BitmapFactory.decodeFileDescriptor(fd,null,options);
if(options.mCancel||options.outWidth==-1
||options.outHeight==-1){
returnnull;
}
options.inSampleSize=computeSampleSize(
options,targetSize,maxPixels);
options.inJustDecodeBounds = false;options.inDither = false;
options.inPreferredConfig=Bitmap.Config.ARGB_8888;
bitmap=BitmapFactory.decodeFileDescriptor(fd,null,options);
}catch(IOExceptionex){
Log.e(TAG,"",ex);
}
}if (kind == Images.Thumbnails.MICRO_KIND) {
//nowwemakeita"squarethumbnail"forMICRO_KINDthumbnail
bitmap=extractThumbnail(bitmap,
TARGET_SIZE_MICRO_THUMBNAIL,
TARGET_SIZE_MICRO_THUMBNAIL,OPTIONS_RECYCLE_INPUT);
}
returnbitmap;
}/**
*Createavideothumbnailforavideo.Mayreturnnullifthevideois
*corruptortheformatisnotsupported.
*
*@paramfilePaththepathofvideofile
*@paramkindcouldbeMINI_KINDorMICRO_KIND
*/
publicstaticBitmapcreateVideoThumbnail(StringfilePath,intkind){
Bitmapbitmap=null;
MediaMetadataRetrieverretriever=newMediaMetadataRetriever();
try{
retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
retriever.setDataSource(filePath);
bitmap=retriever.captureFrame();
}catch(IllegalArgumentExceptionex){
//Assumethisisacorruptvideofile
}catch(RuntimeExceptionex){
//Assumethisisacorruptvideofile.
}finally{
try{
retriever.release();
}catch(RuntimeExceptionex){
//Ignorefailureswhilecleaningup.
}
}
if(kind==Images.Thumbnails.MICRO_KIND&&bitmap!=null){
bitmap=extractThumbnail(bitmap,
TARGET_SIZE_MICRO_THUMBNAIL,
TARGET_SIZE_MICRO_THUMBNAIL,
OPTIONS_RECYCLE_INPUT);
}
returnbitmap;
}/**
*Createsacenteredbitmapofthedesiredsize.
*
*@paramsourceoriginalbitmapsource
*@paramwidthtargetedwidth
*@paramheighttargetedheight
*/
publicstaticBitmapextractThumbnail(
Bitmapsource,intwidth,intheight){
returnextractThumbnail(source,width,height,OPTIONS_NONE);
}/**
*Createsacenteredbitmapofthedesiredsize.
*
*@paramsourceoriginalbitmapsource
*@paramwidthtargetedwidth
*@paramheighttargetedheight
*@paramoptionsoptionsusedduringthumbnailextraction
*/
publicstaticBitmapextractThumbnail(
Bitmapsource,intwidth,intheight,intoptions){
if(source==null){
returnnull;
}float scale;
if(source.getWidth()<source.getHeight()){
scale=width/(float)source.getWidth();
}else{
scale=height/(float)source.getHeight();
}
Matrixmatrix=newMatrix();
matrix.setScale(scale,scale);
Bitmapthumbnail=transform(matrix,source,width,height,
OPTIONS_SCALE_UP|options);
returnthumbnail;
}/*
*ComputethesamplesizeasafunctionofminSideLength
*andmaxNumOfPixels.
*minSideLengthisusedtospecifythatminimalwidthorheightofa
*bitmap.
*maxNumOfPixelsisusedtospecifythemaximalsizeinpixelsthatis
*tolerableintermsofmemoryusage.
*
*Thefunctionreturnsasamplesizebasedontheconstraints.
*BothsizeandminSideLengthcanbepassedinasIImage.UNCONSTRAINED,
*whichindicatesnocareofthecorrespondingconstraint.
*Thefunctionsprefersreturningasamplesizethat
*generatesasmallerbitmap,unlessminSideLength=IImage.UNCONSTRAINED.
*
*Also,thefunctionroundsupthesamplesizetoapowerof2ormultiple
*of8becauseBitmapFactoryonlyhonorssamplesizethisway.
*Forexample,BitmapFactorydownsamplesanimageby2eventhoughthe
*requestis3.SoweroundupthesamplesizetoavoidOOM.
*/
privatestaticintcomputeSampleSize(BitmapFactory.Optionsoptions,
intminSideLength,intmaxNumOfPixels){
intinitialSize=computeInitialSampleSize(options,minSideLength,
maxNumOfPixels);int roundedSize;
if(initialSize<=8){
roundedSize=1;
while(roundedSize<initialSize){
roundedSize<<=1;
}
}else{
roundedSize=(initialSize+7)/8*8;
}return roundedSize; }
private static int computeInitialSampleSize(BitmapFactory.Options options,
intminSideLength,intmaxNumOfPixels){
doublew=options.outWidth;
double h = options.outHeight;int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
(int)Math.ceil(Math.sqrt(w*h/maxNumOfPixels));
intupperBound=(minSideLength==UNCONSTRAINED)?128:
(int)Math.min(Math.floor(w/minSideLength),
Math.floor(h / minSideLength));if (upperBound < lowerBound) {
//returnthelargeronewhenthereisnooverlappingzone.
returnlowerBound;
}if ((maxNumOfPixels == UNCONSTRAINED) &&
(minSideLength==UNCONSTRAINED)){
return1;
}elseif(minSideLength==UNCONSTRAINED){
returnlowerBound;
}else{
returnupperBound;
}
}/**
*MakeabitmapfromagivenUri,minimalsidelength,andmaximumnumberofpixels.
*Theimagedatawillbereadfromspecifiedpfdifit'snotnull,otherwise
*anewinputstreamwillbecreatedusingspecifiedContentResolver.
*
*ClientsareallowedtopasstheirownBitmapFactory.Optionsusedforbitmapdecoding.A
*newBitmapFactory.Optionswillbecreatedifoptionsisnull.
*/
privatestaticBitmapmakeBitmap(intminSideLength,intmaxNumOfPixels,
Uriuri,ContentResolvercr,ParcelFileDescriptorpfd,
BitmapFactory.Optionsoptions){
Bitmapb=null;
try{
if(pfd==null)pfd=makeInputStream(uri,cr);
if(pfd==null)returnnull;
if (options == null) options = new BitmapFactory.Options();FileDescriptor fd = pfd.getFileDescriptor();
options.inSampleSize=1;
options.inJustDecodeBounds=true;
BitmapFactory.decodeFileDescriptor(fd,null,options);
if(options.mCancel||options.outWidth==-1
||options.outHeight==-1){
returnnull;
}
options.inSampleSize=computeSampleSize(
options,minSideLength,maxNumOfPixels);
options.inJustDecodeBounds = false;options.inDither = false;
options.inPreferredConfig=Bitmap.Config.ARGB_8888;
b=BitmapFactory.decodeFileDescriptor(fd,null,options);
}catch(OutOfMemoryErrorex){
Log.e(TAG,"Gotoomexception",ex);
returnnull;
}finally{
closeSilently(pfd);
}
returnb;
}private static void closeSilently(ParcelFileDescriptor c) {
if(c==null)return;
try{
c.close();
}catch(Throwablet){
//donothing
}
}private static ParcelFileDescriptor makeInputStream(
Uriuri,ContentResolvercr){
try{
returncr.openFileDescriptor(uri,"r");
}catch(IOExceptionex){
returnnull;
}
}/**
*TransformsourceBitmaptotargetedwidthandheight.
*/
privatestaticBitmaptransform(Matrixscaler,
Bitmapsource,
inttargetWidth,
inttargetHeight,
intoptions){
booleanscaleUp=(options&OPTIONS_SCALE_UP)!=0;
boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0;int deltaX = source.getWidth() - targetWidth;
intdeltaY=source.getHeight()-targetHeight;
if(!scaleUp&&(deltaX<0||deltaY<0)){
/*
*Inthiscasethebitmapissmaller,atleastinonedimension,
*thanthetarget.Transformitbyplacingasmuchoftheimage
*aspossibleintothetargetandleavingthetop/bottomor
*left/right(orboth)black.
*/
Bitmapb2=Bitmap.createBitmap(targetWidth,targetHeight,
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b2);int deltaXHalf = Math.max(0, deltaX / 2);
intdeltaYHalf=Math.max(0,deltaY/2);
Rectsrc=newRect(
deltaXHalf,
deltaYHalf,
deltaXHalf+Math.min(targetWidth,source.getWidth()),
deltaYHalf+Math.min(targetHeight,source.getHeight()));
intdstX=(targetWidth-src.width())/2;
intdstY=(targetHeight-src.height())/2;
Rectdst=newRect(
dstX,
dstY,
targetWidth-dstX,
targetHeight-dstY);
c.drawBitmap(source,src,dst,null);
if(recycle){
source.recycle();
}
returnb2;
}
floatbitmapWidthF=source.getWidth();
float bitmapHeightF = source.getHeight();float bitmapAspect = bitmapWidthF / bitmapHeightF; float viewAspect = (float) targetWidth / targetHeight;
if (bitmapAspect > viewAspect) {
floatscale=targetHeight/bitmapHeightF;
if(scale<.9F||scale>1F){
scaler.setScale(scale,scale);
}else{
scaler=null;
}
}else{
floatscale=targetWidth/bitmapWidthF;
if(scale<.9F||scale>1F){
scaler.setScale(scale,scale);
}else{
scaler=null;
}
}Bitmap b1;
if(scaler!=null){
//thisisusedforminithumbandcrop,sowewanttofilterhere.
b1=Bitmap.createBitmap(source,0,0,
source.getWidth(),source.getHeight(),scaler,true);
}else{
b1=source;
}if (recycle && b1 != source) {
source.recycle();
}int dx1 = Math.max(0, b1.getWidth() - targetWidth); int dy1 = Math.max(0, b1.getHeight() - targetHeight);
Bitmap b2 = Bitmap.createBitmap(
b1,
dx1/2,
dy1/2,
targetWidth,
targetHeight);if (b2 != b1) {
if(recycle||b1!=source){
b1.recycle();
}
}return b2; }
/**
*SizedThumbnailBitmapcontainsthebitmap,whichisdownsampledeitherfrom
*thethumbnailinexiforthefullimage.
*mThumbnailData,mThumbnailWidthandmThumbnailHeightaresettogetheronlyifmThumbnail
*isnotnull.
*
*Thewidth/heightofthesizedbitmapmaybedifferentfrommThumbnailWidth/mThumbnailHeight.
*/
privatestaticclassSizedThumbnailBitmap{
publicbyte[]mThumbnailData;
publicBitmapmBitmap;
publicintmThumbnailWidth;
publicintmThumbnailHeight;
}/**
*CreatesabitmapbyeitherdownsamplingfromthethumbnailinEXIForthefullimage.
*ThefunctionsreturnsaSizedThumbnailBitmap,
*whichcontainsadownsampledbitmapandthethumbnaildatainEXIFifexists.
*/
privatestaticvoidcreateThumbnailFromEXIF(StringfilePath,inttargetSize,
intmaxPixels,SizedThumbnailBitmapsizedThumbBitmap){
if (filePath == null) return;ExifInterface exif = null;
byte[]thumbData=null;
try{
exif=newExifInterface(filePath);
if(exif!=null){
thumbData=exif.getThumbnail();
}
}catch(IOExceptionex){
Log.w(TAG,ex);
}BitmapFactory.Options fullOptions = new BitmapFactory.Options();
BitmapFactory.OptionsexifOptions=newBitmapFactory.Options();
intexifThumbWidth=0;
int fullThumbWidth = 0;// Compute exifThumbWidth.
if(thumbData!=null){
exifOptions.inJustDecodeBounds=true;
BitmapFactory.decodeByteArray(thumbData,0,thumbData.length,exifOptions);
exifOptions.inSampleSize=computeSampleSize(exifOptions,targetSize,maxPixels);
exifThumbWidth=exifOptions.outWidth/exifOptions.inSampleSize;
}// Compute fullThumbWidth.
fullOptions.inJustDecodeBounds=true;
BitmapFactory.decodeFile(filePath,fullOptions);
fullOptions.inSampleSize=computeSampleSize(fullOptions,targetSize,maxPixels);
fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;// Choose the larger thumbnail as the returning sizedThumbBitmap.
if(thumbData!=null&&exifThumbWidth>=fullThumbWidth){
intwidth=exifOptions.outWidth;
intheight=exifOptions.outHeight;
exifOptions.inJustDecodeBounds=false;
sizedThumbBitmap.mBitmap=BitmapFactory.decodeByteArray(thumbData,0,
thumbData.length,exifOptions);
if(sizedThumbBitmap.mBitmap!=null){
sizedThumbBitmap.mThumbnailData=thumbData;
sizedThumbBitmap.mThumbnailWidth=width;
sizedThumbBitmap.mThumbnailHeight=height;
}
}else{
fullOptions.inJustDecodeBounds=false;
sizedThumbBitmap.mBitmap=BitmapFactory.decodeFile(filePath,fullOptions);
}
}
}