15

我有一个 Android 应用程序可以拍摄一些照片,重新调整它们的大小并将它们发送到后端服务器。此应用程序在除三星 Galaxy S3 之外的所有其他手机(姜饼和冰淇淋三明治)上运行良好。每当它拍摄照片并尝试重新调整大小时,它就会耗尽内存。最初我认为这是调整大小部分的问题,我用 insamplesize 实现了位图对象并尝试了所有方法,但它仍然不断崩溃。然后我意识到,当应用程序一开始启动时,它会占用大量内存。在 HTC 和所有其他手机上,当我的应用程序启动时,它使用了大约 4 MB,但在 Galaxy S3 上,它使用了大约 30 MG - 几乎是 10 倍。我似乎无法弄清楚是什么导致它消耗了更多的内存。我正在正确回收所有对象和布局(我认为)。

public static void resizeImage(String pathOfInputImage,
        String pathOfOutputImage) {
    try {
        int inWidth = 0;
        int inHeight = 0;
        float factor;

        InputStream in = new FileInputStream(pathOfInputImage);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, options);
        inWidth = options.outWidth;
        inHeight = options.outHeight;

        in.close();
        in = null;

        options = null;         

        if (inWidth > inHeight) {
            factor = (float) inHeight / 480;
        } else {
            factor = (float) inWidth / 480;
        }           
        options = new BitmapFactory.Options();

        Bitmap roughBitmap;

        if(Build.VERSION.SDK_INT < 12) {
            System.gc();
        }

        try
        {
            in = new FileInputStream(pathOfInputImage);
            options.inSampleSize = 4;
            roughBitmap = BitmapFactory.decodeStream(in, null, options);
        }
        catch (OutOfMemoryError e)
        {
                roughBitmap = Utils.fetchRoughBitmap(pathOfInputImage, factor);
            }
        finally
        {
            roughBitmap = Utils.fetchRoughBitmap(pathOfInputImage, factor);
            }

        in.close();
        in = null;

        boolean rotate = false;
        Bitmap rotatedBitmap = null;

        if (isNeedToRotate(pathOfInputImage)) {
            rotate = true;
            Matrix m = new Matrix();

            m.postRotate(90);
            try
            {
                rotatedBitmap = Bitmap.createBitmap(roughBitmap, 0, 0,
                        roughBitmap.getWidth(), roughBitmap.getHeight(), m,
                        true);
            }
            catch (OutOfMemoryError e)
            {
                rotatedBitmap = Bitmap.createBitmap(roughBitmap, 0, 0,
                        roughBitmap.getWidth()/2, roughBitmap.getHeight()/2, m,
                        true);
            }
        }


        if (rotate) {
            Utils.log(TAG, "Rotate Invoked :------->");
            int temp = inHeight;

            inHeight = inWidth;
            inWidth = temp;
        }

        options.inSampleSize = Math.round(factor);

        float dstWidth = (int) inWidth / factor;
        float dstHeight = (int) inHeight / factor;

        Bitmap resizedBitmap;
        if (rotate) {
            try
            {
                resizedBitmap = Bitmap.createScaledBitmap(rotatedBitmap,
                        (int) dstWidth, (int) dstHeight, true);
            }
            catch (OutOfMemoryError e)
            {
                resizedBitmap = Bitmap.createScaledBitmap(rotatedBitmap,
                        (int) dstWidth/2, (int) dstHeight/2, true);
            }

        } else {

            try
            {
                resizedBitmap = Bitmap.createScaledBitmap(roughBitmap,
                    (int) dstWidth, (int) dstHeight, true);
            }
            catch (OutOfMemoryError e)
            {
                resizedBitmap = Bitmap.createScaledBitmap(roughBitmap,
                        (int) dstWidth/2, (int) dstHeight/2, true);
            }
        }

        try 
        {
            FileOutputStream out = new FileOutputStream(pathOfOutputImage);
            resizedBitmap.compress(Bitmap.CompressFormat.PNG, 80, out);
        }
        catch (Exception e) {
            Utils.log("Image", e.getMessage() + e);
        }

        //Free up the memory.
        roughBitmap.recycle();
        resizedBitmap.recycle();

        if (rotatedBitmap != null) {
            rotatedBitmap.recycle();
        }

    } catch (IOException e) {
        Utils.log("Image", e.getMessage() + e);
    }
}


private static Bitmap fetchRoughBitmap(String pathOfInputImage, float factor)
{
    Bitmap roughBitmap = null;
    try
    {
        FileInputStream in = new FileInputStream(pathOfInputImage);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = Math.round(factor);
        roughBitmap = BitmapFactory.decodeStream(in, null, options);
        return roughBitmap;
    }
    catch (OutOfMemoryError mem_error)
    {
        return Utils.fetchRoughBitmap(pathOfInputImage, factor+1);
    }
    catch (FileNotFoundException e)
    {
        //return Utils.fetchRoughBitmap(pathOfInputImage, factor+1);
        return roughBitmap;
    }
}

这段代码运行良好,因为我通过递归循环运行它,直到它没有耗尽内存,但我得到的图像只有大约 300x300 像素,我需要它至少为 640x480。我的问题是:

  1. 谁能告诉我为什么 Galaxy S3 在首次加载我的应用程序时使用高达 90% 的内存,而不是 HTC 手机上的 60%。
  2. 在重新调整大小的代码中是否有任何错误导致我的应用程序内存不足。

任何帮助将不胜感激,因为我已经在这个问题上花了 3 天时间:-(。

4

4 回答 4

9

嗯...我知道这是一个老问题了,但这就是我的想法。

Galaxy S3 使用 xhdpi 密度,因此在启动时分配更大的堆大小,(据我所知,32mb)

但是如果您的 res 目录中没有任何 xhdpi 资源,那么它将调用 drawable 中的任何默认图像或从最接近 xhdpi 密度的 hdpi 调用任何默认图像。

然后它将扩展图像以适应其需要,并且在此阶段扩展图像将占用大量内存,(可能)导致内存不足问题。

您可以通过启用屏幕兼容模式来解决此问题,或者您可以创建(如果默认情况下不存在)drawable-xhdpi,为 xhdpi 设备(如 Galaxy S3 )提供适当的资源。

于 2012-11-07T05:29:59.260 回答
8

哇。数周以来,我们一直在努力解决 S3 上的内存不足问题。最后,应用了该线程中的一种技术。

在我们的应用程序中,我们让 drawable-hdpi 拥有应用程序的所有图像。在大多数手机上,我们没有问题。在 S3 上,该应用程序将占用 2 倍的内存,然后遇到内存不足的问题。

我刚刚创建了与 drawable-hdpi 文件夹具有相同内容的 drawable-xhdpi 文件夹并在 S3 上运行它。立即注意到内存占用是 1/2,并且没有内存不足的问题。

这是一次令人沮丧的经历。希望其他人知道寻找这个线程并节省数小时的调试时间。

谢谢

于 2013-10-31T19:26:59.093 回答
5

我自己解决了这个问题。内存不足的应用程序不必对上述代码执行任何操作。我添加了 MAT,我看到位图对象从以前的视图中保持活动状态。所以我从视图中删除了图形,它解决了内存问题。

于 2012-09-11T20:09:38.157 回答
0
I overwrote the onDestroy method and added the following line: layout.setBackgroundDrawable(null);. Please let me know if this worked or didn't work for you. 

– 用户881148

酷,它对我有用......在布局上添加了一些背景图片。从位图转换后,我不使用 BitmapDrawables 或 Bitmap,而是使用 Drawables。

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.outerspace);
Drawable bitDraw = new BitmapDrawable(getResources(), bitmap);

main.setBackgroundDrawable(bitDraw);

我的问题是正在使用 S3 的所有设备(这很烦人)。但该应用程序现已启动并运行!谢谢...user881148

于 2013-04-02T01:37:42.070 回答