1

在我正在开发的应用程序中,用户输入的一部分是一系列图像。其中一些原始形式可能有 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

这是我调整图像大小的地方

private Bitmap getScaledBitmap(String picturePath, int newWidth, int newHeight){
    /* Size */
    BitmapFactory.Options sizeOptions = new BitmapFactory.Options();
    sizeOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(picturePath, sizeOptions);
    int sampleSize = 1;
    int rawHeight = sizeOptions.outHeight;
    int rawWidth = sizeOptions.outWidth;
    if(rawHeight > newHeight || rawWidth > newWidth){
        /* Find the dimension that needs to change the most */
        int heightRatio = Math.round((float) rawHeight / (float) newHeight);
        int widthRatio = Math.round((float) rawWidth / (float) newWidth);

        sampleSize = (heightRatio > widthRatio ? heightRatio : widthRatio);
    }//if(raw image is wider or taller than it should be){reduce size so neither is too large}

    sizeOptions.inJustDecodeBounds = false;//Load pixels for display.
    sizeOptions.inSampleSize = sampleSize;//Set shrink factor.
    Bitmap scaledBitmap = BitmapFactory.decodeFile(picturePath, sizeOptions);

    /* Rotation */
    int rotation = 1;
    try{
        ExifInterface exif = new ExifInterface(picturePath);
        rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
    }
    catch(IOException e){
        e.printStackTrace();
    }
    int rotationInDegrees = 0;
    if(rotation == ExifInterface.ORIENTATION_ROTATE_90)
        rotationInDegrees = 90;
    else if(rotation == ExifInterface.ORIENTATION_ROTATE_180)
        rotationInDegrees = 180;
    else if(rotation == ExifInterface.ORIENTATION_ROTATE_270)
        rotationInDegrees = 270;

    Matrix matrix = new Matrix();
    if(rotation != 0f)
        matrix.preRotate(rotationInDegrees);

    return Bitmap.createBitmap(scaledBitmap, 0, 0, 
                scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
}// method: getScaledBitmap(String, int, int)

在我开始收到关于这个问题如此普遍的评论之前,我会指出我没有显示这些图像,所以我并不是想把所有这些都保存在内存中。我需要保留大图像,因为用户希望能够放大图片,但我正在调整它们的大小,因为它们不需要太大。我在 SO 上看到的关于图像和 OOM 错误的几乎所有其他解决方案都不适用于我对多个图像的背靠背访问。

所以就像我说的,我在保存后回收每个位图,但它们似乎仍在使用内存。知道我错过了什么吗?

4

2 回答 2

1

你不是scaledBitmap在回收getScaledBitmap。解决这个问题应该会有所帮助。更改此行:

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;
于 2013-08-14T21:09:39.010 回答
1

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

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

  1. 使用单个线程进行图像处理。

  2. 总是尽快回收不再需要的旧位图。GC 确实可以帮助您,但这也可以帮助您,它甚至可以在预蜂窝设备上工作。

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

  4. 将图像下采样到您需要的最小尺寸,并且永远不要假设内存对于任何给定的图像都足够大(除非您 100% 确定图像很小)。

  5. 请记住,就每个应用程序的 RAM(堆大小)而言,对 android 设备的要求仍然非常低 - 每个应用程序的最低要求仍然是 16MB。

  6. 您可以在清单中使用 android:largeHeap="true" ,但这并不意味着您将获得多少,如果有的话。

于 2014-02-03T10:18:26.673 回答