创建Bitmap时出现java.lang.OutOfMemoryError错误

4

你好,我正在创建一款图像编辑应用程序,我正在为画布和ImageView创建位图

在这里,我在ImageView中使用了一个2000x2000尺寸、大小为385kb的图像文件

这是图片enter image description here

我的问题是,第一次我用其他小图像创建位图并编辑图像时有时会成功,但第二次使用上述大小和尺寸时会生成java.lang.OutOfMemoryError :(

以下是相关方法代码:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // initialize variable
        init();

        // initialize view
        initView();

        // initialize view's event listener
        initEventListener();

        setImageToImageView(ims);

    }  

private void init() {

        context = MainActivity.this;
        db_colorImage = new DB_ColorImage(context);

        AppUtils.setColorPreference(MainActivity.this, AppConstant.PREF_COLOR_PIC, "FF0000");
        AppUtils.setComboColorPreference(MainActivity.this, AppConstant.PREF_COMBO, 1);

        openCatId = getIntent().getIntExtra("open_cat_id", -1);
        openCat = getIntent().getStringExtra("image_categoryname");
        openImgName = getIntent().getStringExtra("img_name");

        db_colorImage.openDB();
        db_colorImage.startTransaction();
        assetsImgxx = db_colorImage.getXXX(openCatId);
        db_colorImage.successfullTransaction();
        db_colorImage.completeTransaction();
        db_colorImage.closeDB();


        assetsAndSdImage = new ArrayList<>(getIntent().getStringArrayListExtra("again"));           // aa list edit karelu with sdcard image

        try {

            ims = getAssets().open(openCat + "/" + openImgName);
        } catch (IOException e) {
            File getFromSd = new File(AppUtils.appFolder() + File.separator + openImgName);
            try {
                ims = new FileInputStream(getFromSd);
            } catch (FileNotFoundException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }

    }  

    public void setImageToImageView(InputStream ims) {
        Drawable d = Drawable.createFromStream(ims, null);

        BitmapDrawable drawable = (BitmapDrawable) d;

        Bitmap bmp = drawable.getBitmap();


        if (bmp != null) {
            _alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888); // here i used ARGB_8888 because jnibitmap.cpp also use ARGB_8888. If we do not use this it wont color on image
        }

        Canvas canvas = new Canvas(_alteredBitmap);

        Paint paint = new Paint();

        Matrix matrix = new Matrix();

        canvas.drawBitmap(bmp, matrix, paint);

        imageView.setImageBitmap(_alteredBitmap);  // we can also use imageView.setImageResource(R.drawable.test_image);

        // ******* clear bitmap after use *******
        // check after done this step it will not produce any problem
        if (bmp != null) {
            bmp.recycle();
            bmp = null;
        }
    }  

_alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);

在这行代码中,我遇到了异常,以下是日志信息:

01-27 19:12:33.590 27252-27252/? E/AndroidRuntime: FATAL EXCEPTION: main
                                                   java.lang.OutOfMemoryError
                                                       at android.graphics.Bitmap.nativeCreate(Native Method)
                                                       at android.graphics.Bitmap.createBitmap(Bitmap.java:640)
                                                       at android.graphics.Bitmap.createBitmap(Bitmap.java:620)
                                                       at com.yptech.myfloodfilldemo.ui.MainActivity.setImageToImageView(MainActivity.java:387)
                                                       at com.yptech.myfloodfilldemo.ui.MainActivity.onCreate(MainActivity.java:96)
                                                       at android.app.Activity.performCreate(Activity.java:5008)
                                                       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
                                                       at android.app.ActivityThread.access$600(ActivityThread.java:130)
                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
                                                       at android.os.Handler.dispatchMessage(Handler.java:99)
                                                       at android.os.Looper.loop(Looper.java:137)
                                                       at android.app.ActivityThread.main(ActivityThread.java:4745)
                                                       at java.lang.reflect.Method.invokeNative(Native Method)
                                                       at java.lang.reflect.Method.invoke(Method.java:511)
                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
                                                       at dalvik.system.NativeStart.main(Native Method)  

编辑图片后,我保存这张图片,所以不希望降低图片质量,因此我使用了Bitmap.Config.ARGB_8888。我尝试了许多解决方案,如在位图上android开发者文档中提到的方法,但它对我没有用。

OutOfMemoryError是否取决于图像大小和尺寸???如何解决createBitmap时的OutOfMemoryError问题?

当我使用largeHeap时,它可以正常工作,但是使用android:largeHeap="true"是否更好?

提前感谢 :)


1
https://dev59.com/pG025IYBdhLWcg3w9qyQ - Danieboy
使用385kb的图像。您是指图像文件吗?是JPG文件吗? - greenapps
@GreenApps 是的,358kb 的 jpg 文件。 - Harin Kaklotar
那就修改你的帖子吧,我会这样建议。 - greenapps
@greenapps,我添加了一个样例图片。当我第二次创建bitmap(第一次创建bitmap并填充颜色并保存图像,然后返回并打开此活动代码创建bitmap)时,我遇到了异常:(。我认为我必须管理内存以便第二次创建bitmap,因为在第二次启动此活动之前我必须清除此应用程序的所有缓存。你认为呢? - Harin Kaklotar
我不知道为什么有人会踩这个问题?因为问题标题和其他人一样普通吗?但在我的情况下,我有不同的场景。 - Harin Kaklotar
4个回答

2

在使用位图之前优化它。

Bitmap bitmap = MediaOptimize.reduceResolution(mediaPath, width, height);

功能:

public Bitmap reduceResolution(String filePath, int viewWidth, int viewHeight) {
    int reqHeight = viewHeight;
    int reqWidth = viewWidth;

    BitmapFactory.Options options = new BitmapFactory.Options();

    // First decode with inJustDecodeBounds=true to check dimensions
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath, options);

    double viewAspectRatio = 1.0 * viewWidth/viewHeight;
    double bitmapAspectRatio = 1.0 * options.outWidth/options.outHeight;

    if (bitmapAspectRatio > viewAspectRatio)
        reqHeight = (int) (viewWidth/bitmapAspectRatio);
    else
        reqWidth = (int) (viewHeight * bitmapAspectRatio);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    System.gc();                                        // TODO - remove explicit gc calls
    return BitmapFactory.decodeFile(filePath, options);
}


private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }
    return inSampleSize;
}

嗨,但是在降低分辨率之后,我也失去了图像质量。 - Harin Kaklotar
嗨,我正在使用InputStream而不是BitmapFactory.decodeFile在reduceResolution方法中与BitmapFactory.decodeStream一起。然后,在结束时它会返回空位图。 - Harin Kaklotar
每个设备都有宽度和高度的分辨率限制。在达到这个极限之前使用它,你不会看到图像质量的明显降低。 - Jaydev

1

我正在使用Glide(https://bumptech.github.io/glide/)来高效地加载大型位图。在从设备加载照片的情况下,Glide还会为您处理正确的旋转。

val myUri = ...
val neededHeight = 600
val neededWidth = 800

val bitmap = GlideApp
                .with(context)
                .asBitmap()
                .load(myUri)
                .override(neededWidth, neededHeight)
                .submit()
                .get()

-1

图片太大了。我建议缩小图片或使用Picasso库更好地处理内存问题和图像缓存。


图像加载不是我的问题,根据您的建议,我可以使用那些库来加载图像,但我想要创建位图。感谢您的帮助。 - Harin Kaklotar

-1

试试这个,只需将其添加到AndroidManifest.xml中即可。

<application
    ...
    android:largeHeap="true">
    <activity
        android:name=".MainActivity"
        ...


2
在优化不良的应用程序中将largeHeap设置为true是非常糟糕的做法。首先优化您的应用程序,如果您做得正确,您可能不需要largeHeap属性。每个手机都应该可以平稳地加载385kb的图像(就像@Jaydev展示的那样)。https://dev59.com/nF0a5IYBdhLWcg3wVHIm - Karol Żygłowicz
@KarolŻygłowicz,你说得没错,但我制作了一个简单的应用程序并在ImageView中加载了这张图片(我在问题中发布的),但它没有加载,应用程序就关闭了。 - Harin Kaklotar
1
@Hari Kaklotar。我不确定您想通过质量实现什么,因为您正在尝试使用Bitmap.Config.ARGB_8888显示2000x2000图像。根据文档,每个像素都存储在4个字节中。因此,200020004字节=15.25 Mb的数据。这就是为什么您会遇到OutOfMemoryError的原因,因为对于应该重量为385kb的单个图像来说,这太多了。 - Karol Żygłowicz
@KarolŻygłowicz 我正在尝试创建一个颜色填充应用程序,因此我正在使用[库](https://github.com/mar3kk/threekkapps_library)来填充图像中的颜色,所以根据这个库,我必须设置Bitmap.Config.ARGB_8888来填充颜色到我的图像中以获得高质量。这就是为什么我正在使用这个值创建位图,并且出现了异常:( - Harin Kaklotar
Simon,谢谢你的回复,它有效了。非常感谢你的帮助 :) 但是根据@KarolŻygłowicz的说法,使用android:largeHeap="true"不是一个好主意。 - Harin Kaklotar

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