[ 转]内存管理 --- 避免出现 bitmap内存限制 OUT OF MEMORY 的一种方法
?
在编写Android程序的时候,我们总是难免会碰到OOM(OUT OF MEMORY)的错误,那
么这个错误究竟是怎么来的呢,可以先看一下这篇文章 ANDROID BITMAP 内存限制
OOM,OUT OF MEMORY。?
?
这里,我使用 Gallery 来举例,在模拟器中,不会出现 OOM 错误,但是,一旦把程序运
行到真机里,图片文件一多,必然会出现OOM,我们通过做一些额外的处理来避免。?
?
1.创建一个图片缓存对象HashMap dataCache,integer对应Adapter中的位置position,我
们只用缓存处在显示中的图片,对于之外的位置,如果 ?dataCache 中有对应的图片,我
们需要进行回收内存。在这个例子中,Adapter 对象的 getView 方法首先判断该位置是否
有缓存的 ?bitmap,如果没有,则解码图片(bitmapDecoder.getPhotoItem,BitmapDecoder
类见后面)并返回 bitmap ?对象,设置 dataCache ?在该位置上的 bitmap 缓存以便之后使
用;若是该位置存在缓存,则直接取出来使用,避免了再一次调用底层的解码图像需要
的内存开销。有时为了提高 ?Gallery的更新速度,我们还可以预存储一些位置上的bitmap,
比如存储显示区域位置外向上3个向下 3个位置的bitmap,这样上或下滚动 ?Gallery时可
以加快getView的获取。
?
?
@Override public void onItemSelected(AdapterView<?> parent, View view, int position,long id) { releaseBitmap(); Log.v(TAG, "select id:"+ id); } private void releaseBitmap(){ //在这,我们分别预存储了第一个和最后一个可见位置之外的3个位置的bitmap //即dataCache中始终只缓存了(M=6+Gallery当前可见view的个数)M个bitmap int start = mGallery.getFirstVisiblePosition()-3; int end = mGallery.getLastVisiblePosition()+3; Log.v(TAG, "start:"+ start); Log.v(TAG, "end:"+ end); //释放position<start之外的bitmap资源 Bitmap delBitmap; for(int del=0;del<start;del++){ delBitmap = dateCache.get(del); if(delBitmap != null){ //如果非空则表示有缓存的bitmap,需要清理 Log.v(TAG, "release position:"+ del); //从缓存中移除该del->bitmap的映射 dateCache.remove(del); delBitmap.recycle(); } } freeBitmapFromIndex(end); } /** * 从某一位置开始释放bitmap资源 * @param index */ private void freeBitmapFromIndex(int end) { //释放之外的bitmap资源 Bitmap delBitmap; for(int del =end+1;del<dateCache.size();del++){ delBitmap = dateCache.get(del); if(delBitmap != null){ dateCache.remove(del); delBitmap.recycle(); Log.v(TAG, "release position:"+ del); } } }?
经过这些额外的操作,有效的避免了OOM的问题。?
?
内存溢出的解决办法?
?
?
在模拟器上给 gallery 放入图片的时候,出现 java.lang.OutOfMemoryError: bitmap size?
exceeds VM budget 异常,图像大小超过了RAM内存。 ??
?
? ? ? 模拟器RAM比较小,只有8M内存,当我放入的大量的图片(每个100 多 K左右),
就出现上面的原因。由于每张图片先前是压缩的情况。放入到 Bitmap的时候,大小会变
大,导致超出RAM内存,具体解决办法如下: ??
?
//解决加载图片 内存溢出的问题 ??
? ? ? ? ? ? ? ? ? ? ?//Options 只保存图片尺寸大小,不保存图片到内存 ??
? ? ? ? ? ? ? ? ?BitmapFactory.Options opts = new BitmapFactory.Options(); ??
? ? ? ? ? ? ? ? ?//缩放的比例,缩放是很难按准备的比例进行缩放的,其值表明缩放的倍数,
SDK中建议其值是 2的指数值,值越大会导致图片不清晰 ??
? ? ? ? ? ? ? ? ?opts.inSampleSize = 4; ??
? ? ? ? ? ? ? ? ?Bitmap bmp = null; ??
? ? ? ? ? ? ? ? ?bmp = BitmapFactory.decodeResource(getResources(),?
mImageIds[position],opts); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?
? ? ? ? ? ? ? ? ?... ? ? ? ? ? ? ? ??
?
? ? ? ? ? ? ? ? //回收 ??
? ? ? ? ? ? ? ? ?bmp.recycle(); ??
?
通过上面的方式解决了,但是这并不是最完美的解决方式。
?
优化 Dalvik虚拟机的堆内存分配
?
?
对于 Android 平台来说,其托管层使用的 Dalvik Java VM 从目前的表现来看还有很多地
方可以优化处理,比如我们在开发一些大型游戏或耗资源的应用中可能考虑手动干涉 GC
处理,使用 ?dalvik.system.VMRuntime 类提供的 setTargetHeapUtilization 方法可以增强
程序堆内存的处理效率。当然具体 原理我们可以参考开源工程,这里我们仅说下使用方
法: ? ?private final static float TARGET_HEAP_UTILIZATION = 0.75f; 在程序onCreate时
就可以调用 ?VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);?
即可。 ??
?
Android 堆内存也可自己定义大小 ??
?
?
? 对于一些 Android 项目,影响性能瓶颈的主要是 Android 自己内存管理机制问题,目
前手机厂商对 RAM 都比较吝啬,对于软件的流畅性来说 RAM 对 性能的影响十分敏感,
除了 优化 Dalvik 虚拟机的堆内存分配外,我们还可以强制定义自己软件的对内存大小,
?
我们使用Dalvik提供的 ?dalvik.system.VMRuntime类来设置最小堆内存为例: ??
?
private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ; ??
?
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //设置最小 heap 内
存为6MB大小。当然对于内存吃紧来说还可以通过手动干涉GC去处理 ??
?
?
bitmap 设置图片尺寸,避免 内存溢出 ?OutOfMemoryError的优化方法 ??
★android 中用bitmap 时很容易内存溢出,报如下错误: Java.lang.OutOfMemoryError :?
bitmap size exceeds VM budget ??
?
● 主要是加上这段: ??
BitmapFactory.Options options = new BitmapFactory.Options(); ??
? ? ? ? ? ? ? ? ?options.inSampleSize = 2; ??
?
● eg1:(通过Uri取图片) ??
private ImageView preview; ??
BitmapFactory.Options options = new BitmapFactory.Options(); ??
? ? ? ? ? ? ? ? ? ? ?options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来
的四分之一 ??
? ? ? ? ? ? ? ? ? ? ?Bitmap bitmap = BitmapFactory.decodeStream(cr ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.openInputStream(uri), null, options); ??
? ? ? ? ? ? ? ? ? ? ?preview.setImageBitmap(bitmap); ??
以上代码可以优化内存溢出,但它只是改变图片大小,并不能彻底解决内存溢出。 ??
● eg2:(通过路径去图片) ??
private ImageView preview; ??
private String fileName= "/sdcard/DCIM/Camera/2010-05-14 16.01.44.jpg"; ??
BitmapFactory.Options options = new BitmapFactory.Options(); ??
? ? ? ? ? ? ? ? ?options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的
四分之一 ??
? ? ? ? ? ? ? ? ? ? ? ? ?Bitmap b = BitmapFactory.decodeFile(fileName, options); ??
? ? ? ? ? ? ? ? ? ? ? ? ?preview.setImageBitmap(b); ??
? ? ? ? ? ? ? ? ? ? ? ? ?filePath.setText(fileName); ??
?
?