【问题标题】:Android App OutOfMemory ErrorAndroid 应用程序 OutOfMemory 错误
【发布时间】:2016-05-08 09:15:07
【问题描述】:

我正在编写一个需要大量内存的 Android 应用程序,并且遇到内存不足的问题。该应用程序有两个活动。主要活动使用一系列按钮,这些按钮使用事件处理程序将不同的图像加载到第二个活动中的 ImageViews。当用户按下 Android 后退按钮时,应用返回到主 Activity。

我遇到的问题是,应用程序最终会因 OutOfMemory 错误而崩溃。

下图显示了当我在两个活动之间来回切换时我的应用程序的内存使用情况。截取此屏幕截图后,我再次尝试通过其中一个按钮打开第二个活动,但错误再次发生。 您会注意到最后一个驼峰离其他驼峰更远。这是因为我停下来截屏一秒钟,期望下一次尝试会崩溃。没有的时候我又截图了。

我认为可以通过显式强制活动在不使用它们时释放内存来解决问题。这样做的问题是我不知道如何做到这一点,并且在网上找不到任何明确的解释来详细说明如何做到这一点。

以下是 Image View 活动的代码示例:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageView;
import android.widget.TableRow;

import java.io.File;

public class ViewPost extends AppCompatActivity {
    Context appContext = this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_image);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

    @Override
    protected void onResume() {
        super.onResume();

        new Thread(new Runnable() {
            @Override
            public void run() {
                final ImageView imgPhoto = (ImageView) findViewById(R.id.imgPhoto);
                final TableRow photoLayout = (TableRow) findViewById(R.id.photoLayout);

                // Get the width of the device screen.
                Point screenDimensionFinder = new Point();
                getWindowManager().getDefaultDisplay().getSize(screenDimensionFinder);
                final int screenWidth = screenDimensionFinder.x;

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        File imageFolder = new File(String.valueOf(appContext.getExternalFilesDir(
                                Environment.DIRECTORY_PICTURES)));
                        File photoFile = new File(imageFolder, "P" + Integer.toString(postId) +
                                ".jpg");
                        if(photoFile.exists()) {
                            Bitmap image = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
                            imgPhoto.setImageBitmap(resizeBitmap(image, screenWidth));
                        }
                        else {
                            photoLayout.setVisibility(View.GONE);
                        }
                    }
                });
            }
        }).start();
    }

    private Bitmap resizeBitmap(Bitmap image, int screenWidth)
    {
        // Resize the bitmap so that it fits on the screen, while preserving its aspect ratio.
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();
        int imageNewHeight = (imageHeight * screenWidth) / imageWidth;
        image = Bitmap.createScaledBitmap(image, screenWidth, imageNewHeight, false);
        return image;
    }
}

【问题讨论】:

  • 能把这两个活动的代码贴出来吗!
  • @varunkr 你想看哪一部分?我不想发布所有这些。 (即按钮的事件处理程序,图像处理)
  • 由于您发布的是图像,但很明显它是错误的来源,但是要确定确切的问题,我需要查看代码..
  • 至少需要第二个活动的代码

标签: android out-of-memory


【解决方案1】:

您应该尝试使用此函数来解码文件。此方法使用 inSampleSize 变量加载位图的缩小版本,从而减少内存消耗。

private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

【讨论】:

  • 谢谢!你能解释一下IMAGE_MAX_SIZE是什么吗?这与我在“使用现有位图”标题下的 this page 上看到的功能相同吗?
  • @DavidB 不,那里的 decodeFile 和您的代码中的一个只是使用 BitmapFactory 类中的默认实现,它不关心这一点。在这种方法中,图像被适当地缩放,以防止内存错误。 IMAGE_MAX_SIZE 是您想要将其缩放到的新尺寸。也就是您的 imageView 尺寸。
  • @DavidB 另外,我必须这样说以澄清您对问题的疑问。简单地杀死活动并不能保证内存将被清除。为此,您需要清除不应该执行的进程,而是将其留给操作系统。
  • 很好。问题是,ImageView 匹配设备屏幕的宽度,我想保持图像的纵横比。用这个会不会有问题?
  • 您应该尝试一次以查看图像的外观。纵横比不应该是国际海事组织的问题。质量可能是个问题,尽管您应该尝试确定一下。
【解决方案2】:

我遇到了完全相同的问题。我通过正方形使用毕加索解决了这个问题。 Picasso.with(getContext()).load(imageResId).fit().centerInside().into(imageView);

您将不得不更改加载方式,但它支持加载(文件 f)。

【讨论】:

  • 谢谢,但很遗憾,我不允许在这个项目中使用任何第三方库。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多