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

在编写Android程序的时候,我们总是难免会碰到OOM(OUT OF MEMORY)的错误,那

2013-03-30 14:01 507 查看
在编写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的获取。

Java代码







public View getView(int position, View convertView, ViewGroup parent)

{

if(convertView==null){

LayoutInflater inflater =

LayoutInflater.from(context);

convertView =
inflater.inflate(R.layout.photo_item, null);

holder = new ViewHolder();

holder.photo = (ImageView)
convertView.findViewById(R.id.photo_item_image);

holder.photoTitle = (TextView)
convertView.findViewById(R.id.photo_item_title);

holder.photoDate = (TextView)
convertView.findViewById(R.id.photo_item_date);

convertView.setTag(holder);

}else {

holder = (ViewHolder)
convertView.getTag();

}

cursor.moveToPosition(position);

Bitmap current =
dateCache.get(position);

if(current != null){//如果缓存中已解码

该图片,则直接返回缓存中的图片

holder.photo.setImageBitmap(current);

}else {

current =
bitmapDecoder.getPhotoItem(cursor.getString(1), 2) ;holder.photo.setImageBitmap(current);

dateCache.put(position,
current);

}

holder.photoTitle.setText(cursor.getString(2));

holder.photoDate.setText(cursor.getString(4));

return convertView;

}

}

public View getView(int position, View convertView, ViewGroup parent)
{

if(convertView==null){

LayoutInflater inflater  =

LayoutInflater.from(context);

convertView =
inflater.inflate(R.layout.photo_item, null);

holder = new ViewHolder();

holder.photo = (ImageView)
convertView.findViewById(R.id.photo_item_image);

holder.photoTitle = (TextView)
convertView.findViewById(R.id.photo_item_title);

holder.photoDate = (TextView)
convertView.findViewById(R.id.photo_item_date);

convertView.setTag(holder);

}else {

holder = (ViewHolder)
convertView.getTag();

}

cursor.moveToPosition(position);

Bitmap current =
dateCache.get(position);

if(current != null){//如果缓存中已解码
该图片,则直接返回缓存中的图片

holder.photo.setImageBitmap(current);

}else {

current =
bitmapDecoder.getPhotoItem(cursor.getString(1), 2) ;holder.photo.setImageBitmap(current);

dateCache.put(position,
current);

}

holder.photoTitle.setText(cursor.getString(2));

holder.photoDate.setText(cursor.getString(4));

return convertView;

}

}


BitmapDecoder.class

Java代码







package com.wuyi.bestjoy;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Matrix;

public class BitmapDecoder {

private static final String TAG = "BitmapDecoder";

package com.wuyi.bestjoy;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Matrix;

public class BitmapDecoder {

private static final String TAG = "BitmapDecoder";


Java代码







private Context context;

public BitmapDecoder(Context context) {

this.context = context;

}

public Bitmap getPhotoItem(String filepath,int size) {

BitmapFactory.Options options = new
BitmapFactory.Options();

options.inSampleSize=size;

Bitmap bitmap =
BitmapFactory.decodeFile(filepath,options);

bitmap=Bitmap.createScaledBitmap(bitmap, 180,
251, true);//预先缩放,避免实时缩放,可以提高更新率

return bitmap;

}

}

private Context context;

public BitmapDecoder(Context context) {

this.context = context;

}

public Bitmap getPhotoItem(String filepath,int size) {

BitmapFactory.Options options = new
BitmapFactory.Options();

options.inSampleSize=size;

Bitmap bitmap =
BitmapFactory.decodeFile(filepath,options);

bitmap=Bitmap.createScaledBitmap(bitmap, 180,
251, true);//预先缩放,避免实时缩放,可以提高更新率

return bitmap;

}

}


2.由于Gallery控件的特点,总有一个item处于当前选择状态,我们利用此时进行

dataCache中额外不用的bitmap的清理,来释放内存。

Java代码







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

}

}

}

@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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐