加载GridView位图时出现Android OutOfMemory错误

加载GridView位图时出现Android OutOfMemory错误,android,memory,Android,Memory,我正在尝试将一些非常小的图像(平均大小为90kb)加载到Android的gridview中。每当我加载超过9个图像时,我就会遇到内存问题。我曾尝试将图像缩放到较小的尺寸,尽管这在一定程度上有效,但这并不是真正的解决方案,因为图像质量很差 代码如下 private Context mContext; private ArrayList<Bitmap> photos = new ArrayList<Bitmap>(); public Bitmap [] mThumbIds;

我正在尝试将一些非常小的图像(平均大小为90kb)加载到Android的gridview中。每当我加载超过9个图像时,我就会遇到内存问题。我曾尝试将图像缩放到较小的尺寸,尽管这在一定程度上有效,但这并不是真正的解决方案,因为图像质量很差

代码如下

private Context mContext;
private ArrayList<Bitmap> photos = new ArrayList<Bitmap>();
public Bitmap [] mThumbIds;

public ImageAdapter(Context c) {
    mContext = c;
}


public Object getItem(int position) {
    return null;
}

public long getItemId(int position) {
    return 0;
}

public Bitmap scaleBitmap(String imagePath) {
    Bitmap resizedBitmap = null;
    try {
        int inWidth = 0;
        int inHeight = 0;

        InputStream in;

        in = new FileInputStream(imagePath);

        // decode image size (decode metadata only, not the whole image)
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, options);
        in.close();
        in = null;

        // save width and height
        inWidth = options.outWidth;
        inHeight = options.outHeight;

        // decode full image pre-resized
        in = new FileInputStream(imagePath);
        options = new BitmapFactory.Options();
        // calc rought re-size (this is no exact resize)
        options.inSampleSize = Math.max(inWidth/300, inHeight/300);
        // decode full image
        Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);

        // calc exact destination size
        Matrix m = new Matrix();
        RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
        RectF outRect = new RectF(0, 0, 300, 300);
        m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
        float[] values = new float[9];
        m.getValues(values);

        // resize bitmap
        resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return resizedBitmap;
}


public void populateGrid() {
    File sdDir = new File("mnt/sdcard/Pictures");
    File[] sdDirFiles = sdDir.listFiles();
    for(File singleFile : sdDirFiles) {
        String filePath = singleFile.getAbsolutePath();
        Bitmap bmp = scaleBitmap(filePath);
        photos.add(bmp);
    }
    mThumbIds = photos.toArray(new Bitmap[(photos.size())]);
}

// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView;
    if (convertView == null) {  // if it's not recycled, initialize some attributes
        imageView = new ImageView(mContext);
        imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setPadding(8, 8, 8, 8);
    } else {
        imageView = (ImageView) convertView;
    }
    imageView.setImageBitmap(mThumbIds[position]);
    return imageView;
}

@Override
public int getCount() {
    return mThumbIds.length;
}
}
私有上下文mContext;
私有ArrayList照片=新建ArrayList();
公共位图[]mThumbIds;
公共图像适配器(上下文c){
mContext=c;
}
公共对象getItem(int位置){
返回null;
}
公共长getItemId(int位置){
返回0;
}
公共位图缩放位图(字符串图像路径){
位图resizedBitmap=null;
试一试{
int inWidth=0;
int inHeight=0;
输入流输入;
in=新文件输入流(imagePath);
//解码图像大小(仅解码元数据,而不是整个图像)
BitmapFactory.Options=new-BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeStream(in,null,选项);
in.close();
in=null;
//节省宽度和高度
inWidth=options.outWidth;
inHeight=options.outHeight;
//解码预调整大小的完整图像
in=新文件输入流(imagePath);
options=新的BitmapFactory.options();
//计算粗糙度重新调整大小(这不是精确的调整大小)
options.inSampleSize=Math.max(inWidth/300,inHeight/300);
//解码完整图像
位图roughBitmap=BitmapFactory.decodeStream(in,null,options);
//计算准确的目的地大小
矩阵m=新矩阵();
RectF inRect=new RectF(0,0,roughBitmap.getWidth(),roughBitmap.getHeight());
RectF outRect=新的RectF(0,0,300,300);
m、 SetRectRect(inRect、outRect、Matrix.ScaleToFit.CENTER);
浮点[]值=新浮点[9];
m、 获取值(值);
//调整位图大小
resizedBitmap=Bitmap.createScaledBitmap(roughBitmap,(int)(roughBitmap.getWidth()*值[0]),(int)(roughBitmap.getHeight()*值[4]),true);
}catch(filenotfounde异常){
e、 printStackTrace();
}捕获(IOE异常){
e、 printStackTrace();
}
返回resizedBitmap;
}
public void populateGrid(){
文件sdDir=新文件(“mnt/sdcard/Pictures”);
File[]sdDirFiles=sdDir.listFiles();
对于(单个文件:sdDirFiles){
字符串filePath=singleFile.getAbsolutePath();
位图bmp=scaleBitmap(文件路径);
照片。添加(bmp);
}
mThumbIds=photos.toArray(新位图[(photos.size())]);
}
//为适配器引用的每个项目创建新的ImageView
公共视图getView(int位置、视图转换视图、视图组父视图){
图像视图图像视图;
如果(convertView==null){//如果它没有被回收,初始化一些属性
imageView=新的imageView(mContext);
setLayoutParams(新的GridView.LayoutParams(85,85));
imageView.setScaleType(imageView.ScaleType.CENTER\U裁剪);
设置填充(8,8,8,8);
}否则{
imageView=(imageView)convertView;
}
setImageBitmap(mThumbIds[position]);
返回图像视图;
}
@凌驾
public int getCount(){
返回mThumbIds.length;
}
}
两个注意事项:

  • 90kb的压缩图像大小其实并不重要。内存使用由位图的实际分辨率决定——在本例中为300*300*4bpp,因此每个位图大约360k

  • 姜饼有一些缺陷,因为位图内存存储在本机数组(而不是Java堆)中,再加上垃圾收集同时发生。由于这一事实,内存管理器有时需要更长的时间才能意识到位图内存可以重复使用

  • 因此,其后果是:

  • 估计内存使用时,请考虑实际解压缩的位图大小
  • 尽可能多地回收中间位图,以帮助更快地回收内存。例如,如果要缩放位图,请保存对预缩放源的引用,并在缩放完成后回收源(与缩放结果比较,因为可能返回相同的位图)
    如果可以,请在ICS设备上测试代码。然后,您可以使用堆检查工具了解内存使用最多的位置。由于ICS在Java堆上分配位图内存,您将获得位图内存使用情况的准确图片

    位图占用大量内存,如果您已经将图像保存到内存中,则最好只使用文件的路径并将其推送到ImaveView上

    ImageView img = new ImageView(getApplicationContext());;
    
    img.setImageBitmap(BitmapFactory.decodeFile(media.getThumbPath()));
    img.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
    
    myLinearLayout.addView(img);
    

    像这样的东西可能会更好。这样您就不会将所有位图都存储到堆中。

    非常感谢您的回复。我曾尝试过回收位图,但不断收到一个异常,说我试图使用回收的位图。请你举个例子来推荐我应该回收位图的地方。回收位图是很棘手的。确保仅回收尚未连接到视图的实例。你在姜饼上遇到过这些问题吗?如果不是Ginberbread,那么我关于回收利用的建议就不太可能有帮助,更可能的是你只是以某种方式泄露了位图。泄露的内容往往是罪魁祸首。如果是这种情况,使用堆跟踪工具将帮助您确定上下文是否保持活动时间过长。事实上,您持有位图的ArrayList意味着这是一个很好的机会。感谢您的回复。我实际上无法做到这一点,因为位图是从SD卡动态创建和读取的,所以它们不能存储为可绘制的。此外,此注释是不正确的。在某些情况下,ImageView还必须解压缩i