Android 处理多个大位图时内存不足

Android 处理多个大位图时内存不足,android,bitmap,out-of-memory,Android,Bitmap,Out Of Memory,在我正在开发的应用程序中,用户输入的一部分是一系列图像。其中一些可能是4MB大的原始形式。我调整它们的大小并旋转它们,然后将它们保存在设备内存的应用程序部分,供以后使用。我遇到的问题是,即使在保存每个位图后循环使用,我的内存似乎已用完 这是主要的处理方法 private class SaveImagesTask extends AsyncTask<Long, Void, Void>{ @Override protected Void doInBackground(Lo

在我正在开发的应用程序中,用户输入的一部分是一系列图像。其中一些可能是4MB大的原始形式。我调整它们的大小并旋转它们,然后将它们保存在设备内存的应用程序部分,供以后使用。我遇到的问题是,即使在保存每个位图后循环使用,我的内存似乎已用完

这是主要的处理方法

private class SaveImagesTask extends AsyncTask<Long, Void, Void>{
    @Override
    protected Void doInBackground(Long... ids){
        long id = ids[0];
        Iterator<ImageButton> itImg = arrBtnImage.iterator();
        Iterator<TextView> itLbl = arrLblImage.iterator();
        while(itImg.hasNext() && itLbl.hasNext()){
            String imgPath = (String) itImg.next().getTag();
            String imgLbl = itLbl.next().getText().toString().trim();
            String imgName = imgLbl.replace(" ", "_").replace(",", "_");
            imgName += ".jpg";

            if(imgPath != null){
                /* Save resized version of image */
                File dir = getApplicationContext().getFilesDir();
                dir = new File(dir, "temp/" + Long.toString(plantId));
                boolean madeDir = dir.mkdirs();
                File path = new File(dir, imgName);
                Bitmap toSave = getScaledBitmap(imgPath, IMAGE_MAX_SIDE_LENGTH, IMAGE_MAX_SIDE_LENGTH);

                try{
                    BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(path));
                    boolean insertSuccess = toSave.compress(Bitmap.CompressFormat.JPEG, 90, outStream);
                    outStream.close();
                }
                catch(FileNotFoundException e){
                    e.printStackTrace();
                }
                catch(IOException e){
                    e.printStackTrace();
                }
                toSave.recycle();
            }//if
        }//while(more images to process)
    }// method: doInBackground(params)
}// inner class: saveImages extends AsyncTask
在我开始评论这是一个如此普遍的问题之前,我要指出我没有显示这些图像,所以我不想把所有这些都保存在内存中。我需要保留大图片,因为用户希望能够放大图片,但我正在调整它们的大小,因为它们不需要大得离谱。我在上看到的几乎所有其他解决方案,因此对于图像和OOM错误,都不适用于我对多个图像的背对背访问


就像我说的,我会在每个位图保存后循环使用,但它们似乎仍然在使用内存。你知道我遗漏了什么吗?

你没有在
getscaledbimat
中循环使用
scaledbimat
。解决这个问题应该会有帮助。更改此行:

return Bitmap.createBitmap(scaledBitmap, 0, 0, 
            scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
例如:

Bitmap newBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, 
            scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
scaledBitmap.recycle();
return newBitmap;

如果有多个线程处理大型位图,则在某些情况下会占用大量内存

你需要的是根据你的需要找到最好的方法。以下是您可以做和/或需要知道的一些事情:

  • 使用单个线程处理图像

  • 始终尽可能快地回收不再需要的旧位图。GC确实可以帮助您,但这也可以帮助您,它甚至可以在蜂窝设备之前工作

  • 通过NDK进行图像处理(因此,每次图像处理不需要2个位图),例如使用

  • 将图像缩小到所需的最小大小,并且永远不要假设内存对于任何给定的图像都足够大(除非您100%确定图像很小)

  • 请记住,android设备在每个应用程序的RAM(堆大小)方面的要求仍然很低——每个应用程序的最低内存仍然是16MB

  • 您可以在清单中使用android:largeHeap=“true”,但这并不意味着您将获得更多(如果有的话)


  • 你看完了吗?就像我说的,我没有显示它们,也不能将它们调整到只有几百像素。但是是的,我确实读过。对于我正在做的其他一些事情来说,它非常方便,但不是这个。GC不明白吗?应用程序是否会以某种方式移动得太快,GC无法捕捉到它?最终。但是,如果您正在快速浏览许多位图,有时尽早标记位图以供循环使用会有所帮助。从技术上讲,双方的观点都适用于您最初使用的回收。值得一试,但结果是
    newBitmap
    似乎保留了对
    scaledbimat
    的引用。我收到一个错误,上面写着“无法压缩回收的位图”。文档确实说明可能会返回原始对象,但我不认为会是这种情况,因为旋转。我在这个应用程序上的工作大部分在夏天结束时停止,我的工作已经重新集中在相应的网站上。与此同时,我决定缩小图片的尺寸。希望今年夏天能有其他人来继续我的应用工作。我会确保下一个家伙知道你用NDK的答案,希望他能把它(和你的其他建议)结合起来。谢谢你的回答!看起来很有希望。@stsprug是的。我还寻找了一种在“jni世界”(C/C++)上解码的好方法,但我找不到,所以我所做的就是让它得到“java世界”所拥有的,然后我循环使用位图并在“jni世界”上进行工作,完成后,我将结果位图返回到“java世界”。
    Bitmap newBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, 
                scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
    scaledBitmap.recycle();
    return newBitmap;