三星Galaxy S3上的Android应用程序崩溃(内存不足错误)

15

我有一个Android应用程序,可以拍摄一些照片,调整它们的大小并将它们发送到后端服务器。这个应用在所有其他手机上都可以正常工作(Gingerbread 和 Ice cream sandwich),但在三星Galaxy S3上却出现了问题。每当它尝试拍摄照片并调整大小时,就会耗尽内存。起初我认为这是调整大小部分的问题,我使用insamplesize实现了bitmap对象并尝试了各种方法,但仍然一直崩溃。然后我意识到当应用程序首次启动时,它会使用大量内存。在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天时间来解决这个问题 :-(。


你能否添加一些分析代码,以尝试找出程序在哪里膨胀的吗? - Wug
感谢您的迅速回复。实际上我不确定如何添加分析代码。我刚开始学习Android开发,这段代码是交给我的。 - user881148
说实话,我也不确定如何在安卓上做到这一点。我知道System中有一个方法可以返回程序分配的字节数,但我不确定安卓是否支持它。而且它也不是很具体,只能作为代码中触发大量分配的部分的大致测量。不过我认为在这种情况下这就足够了。 - Wug
当应用程序加载时,它会加载一个闪屏图像和一个背景图像。当我在应用程序加载时将这些图像删除时,应用程序运行正常。但是当我把它们留下来时,就会因为内存不足而运行失败。我检查了资源目录,发现我们只有Drawable文件夹中的图片,没有在任何drawable-hdpi、ldpi或mdpi文件夹中的图片。所以设备是否正在尝试将这些图像转换为适合手机分辨率的图像,并且使用了所有内存? - user881148
4个回答

9

嗯...我知道这是一个老问题,但我想说的是:

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

但是,如果您的res目录中没有任何xhdpi资源,则会调用drawable中最接近xhdpi密度的默认图像或hdpi图像。然后它将扩展图像以适应其需求,在此阶段扩展图像将占用大量内存,可能导致内存不足问题。

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


我的应用程序出现了OOM问题,但是通过将构建目标版本更改为4.0而不是2.2,我的问题得到了解决。(尽管我不得不放弃支持4.0以下的设备) - Dan Lee

8

哇,我们在S3上一直为这个内存不足问题苦苦挣扎了几周。最终,在这个帖子中应用了其中的一种技术。

在我们的应用程序中,我们使用drawable-hdpi来保存应用程序的所有图片。在大多数手机上,我们没有遇到任何问题。但是在S3上,应用程序会占用2倍的内存,然后出现内存不足的问题。

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

这是一个令人沮丧的经历。希望其他人知道要寻找这个帖子,以节省数小时的调试时间。

谢谢


屏幕布局在两种分辨率的设备上看起来还好吗? - Abdul Wahab
对我有用,我正在NOTE2上工作,非常感谢 :) - nobalG

5

我自己解决了这个问题。应用程序因内存不足而与上述代码无关。我添加了MAT,发现位图对象仍然存在于之前的视图中。因此,我将图形从视图中移除,解决了内存问题。


1
我覆盖了onDestroy方法并添加了以下行:layout.setBackgroundDrawable(null);。请告诉我这是否对您有用。 - user881148
我已经在我的代码中添加了这行,在onCreate方法中。第一次拍照后,它会在ImageView中显示,但是当返回相机并拍摄新照片时,它会崩溃,就像对你来说一样... - Siva K
在你的代码中,布局是指图像视图或任何其他布局...你在哪个位置添加了这些行,在OnCreate还是Resume等其他地方...请帮帮我... - Siva K
1
这只发生在三星Galaxy S3上,因为该手机的分辨率比其他任何手机都要大得多。我检查了HTC One和所有其他手机,位图图像在所有手机中都被保留,但这不是问题,因为这些手机没有如此大的分辨率图像,因此即使它们始终在内存中,也永远不会耗尽内存。因此,当您在MAT中查找时,请查看哪些对象正在保持活动状态以及从哪个视图查看。然后进入该视图并添加onDestroy方法,并将背景可绘制项设置为null。 - user881148
这里也有同样的问题,即使在使用与Galaxy S3相同密度的Htc One x上运行得非常完美。那么,Galaxy S3的问题是什么呢? - Mustafa Güven
显示剩余3条评论

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

很棒,对我有用...在布局的背景图片上稍作修改。我使用Drawable而不是BitmapDrawables或Bitmap,在从位图转换后使用。

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

main.setBackgroundDrawable(bitDraw);

我的问题是它在除了S3之外的所有设备上都可以运行(这很烦人)。但现在应用程序已经启动并运行了!谢谢...user881148


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接