转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);

}

}

}

相关推荐