Android:java.lang.OutOfMemoryError:无法分配23970828字节的内存,还剩2097152字节和2MB的可用空间,直到OOM。

434

我想从SD卡中显示已存储的Bitmap图像在ImageView中。运行我的应用程序后出现了错误并收到OutOfMemoryError错误消息:

(java.lang.OutOfMemoryError: Failed to allocate a 23970828 byte allocation with 2097152 free bytes and 2MB until OOM)

我不知道为什么会出现内存不足的问题。我认为我的图像大小很大,所以尝试了一些修改。

Iterator<String> it = imageArray.iterator();
while (it.hasNext()) {
  Object element = it.next();
  String objElement = element.toString();
  Log.e("objElement ", " = " + objElement);
  final ImageView imageView = new ImageView (getContext());
  final ProgressBar pBar = new ProgressBar(getContext(), null, 
                                           android.R.attr.progressBarStyleSmall);
  imageView.setTag(it);
  pBar.setTag(it);

  imageView.setImageResource(R.drawable.img_placeholder);
  pBar.setVisibility(View.VISIBLE);

  if (objElement.endsWith(mp3_Pattern)) {
     Log.e("Mp3 ", " ends with ");
     pBar.setVisibility(View.GONE);
     imageView.setImageResource(R.drawable.audio_control);
  }
  if (objElement.endsWith(png_Pattern)) {
     Bitmap bitmap = BitmapFactory.decodeFile(objElement);
     int size = Math.min(bitmap.getWidth(), bitmap.getHeight());
     int x = (bitmap.getWidth() - size) / 2;
     int y = (bitmap.getHeight() - size) / 2;
     Bitmap bitmap_Resul = Bitmap.createBitmap(bitmap, x, y, size, size);
     Log.e("bitmap_Resul "," = "+ bitmap_Resul);

     if (bitmap_Resul != bitmap) {
        bitmap.recycle();
     }
     imageView.setImageBitmap(bitmap_Resul);
     Log.e("png_Pattern ", " ends with ");
     Log.e(" bitmap "," = " + bitmap);
  }

  holder.linearLayout.addView(imageView);
  holder.linearLayout.addView(pBar);

日志信息:

08-27 14:11:15.307    1857-1857/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.example.tazeen.classnkk, PID: 1857
    java.lang.OutOfMemoryError: Failed to allocate a 23970828 byte allocation with 2097152 free bytes and 2MB until OOM
            at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
            at android.graphics.Bitmap.nativeCreate(Native Method)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:812)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:789)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:709)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:634)
            at com.example.tazeen.classnkk.AllPosts_Page$MyListAdapter.getView(AllPosts_Page.java:357)
            at android.widget.AbsListView.obtainView(AbsListView.java:2347)
            at android.widget.ListView.makeAndAddView(ListView.java:1864)
            at android.widget.ListView.fillDown(ListView.java:698)
            at android.widget.ListView.fillFromTop(ListView.java:759)
            at android.widget.ListView.layoutChildren(ListView.java:1659)
            at android.widget.AbsListView.onLayout(AbsListView.java:2151)
            at android.view.View.layout(View.java:15671)
            at android.view.ViewGroup.layout(ViewGroup.java:5038)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
            at android.view.View.layout(View.java:15671)
            at android.view.ViewGroup.layout(ViewGroup.java:5038)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
            at android.view.View.layout(View.java:15671)
            at android.view.ViewGroup.layout(ViewGroup.java:5038)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
            at android.view.View.layout(View.java:15671)
            at android.view.ViewGroup.layout(ViewGroup.java:5038)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
            at android.view.View.layout(View.java:15671)
            at android.view.ViewGroup.layout(ViewGroup.java:5038)
            at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2086)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1843)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5885)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
            at android.view.Choreographer.doCallbacks(Choreographer.java:580)
            at android.view.Choreographer.doFrame(Choreographer.java:550)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5257)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

7
检查图像的像素,宽度不能超过1440,高度不能超过2560。这样做可以避免出现OutOfMemoryError错误。 - Pankaj Lilan
4
你在哪里找到这些数值(1440 X 2560)?有没有参考Android文档? - LCJ
请参见 https://dev59.com/puo6XIcBkEYKwwoYTzNK。 - JFreeman
1
@PankajLilan 你能分享一下参考资料吗? - Arnab Mukherjee
@LCJ 你找到这背后的参考或逻辑了吗? - Arnab Mukherjee
@ArnabMukherjee 不。 - LCJ
32个回答

6
我发现在“drawable”文件夹中的图像会在高清手机上转换为更大的图像。例如,一个200k的图像将被重新采样为更高分辨率,如800k或32000k。迄今为止,我必须自己发现这个堆内存陷阱,并没有看到相关文档。为了避免这种情况,我把所有东西都放在一个drawable-nodpi文件夹中(除了根据特定设备堆使用'BitmapFactory'中的'options')。我不能承担我的应用程序大小因多个drawable文件夹而膨胀,特别是屏幕定义的范围现在如此广泛。棘手的是,Studio现在在项目视图中并没有明确指出“drawable-nodpi”文件夹,它只显示一个“drawable”文件夹。如果你不小心,在studio中将图像拖放到此文件夹时,它实际上不会被放入drawable-nodpi中:
Careful here 'backgroundtest' did not actually go to drawable-nodpi and will 
be resampled higher, such as 4x or 16x of the original dimensions for high def screens.

enter image description here

请确保在对话框中向下点击到nodpi文件夹,因为项目视图不再像以前那样单独显示所有可绘制文件夹,所以它不会立即显示出错的文件夹。我很久以前删除了' drawable',但是Studio在某个时候重新创建了它。

enter image description here


对于任何想要了解drawable-nohdpi的解释的人(如@Androidcoder的答案中所引用的):请参阅https://developer.android.com/training/multiscreen/screendensities#DensityConsiderations - Joe

6
如果android:largeHeap="true"无效,那么:
1: 图像压缩。 我正在使用这个网站
2: 将图像转换为mdpi,hdpi,xhdpi,xxhdpi,xxxhdpi。 我正在使用这个网站
不要移除android:largeHeap="true"!

5
这个对我有用:只需将图片从drawable文件夹移动到drawable-hdpi文件夹即可。

这是什么的解释?为什么这个移动起作用了? - Harun

5
Last but not the least....

Try Method One:

Simple Add these lines of code in the gradle file

dexOptions {
        incremental true
        javaMaxHeapSize "4g"
     }

Example:

android {
    compileSdkVersion XX
    buildToolsVersion "28.X.X"

    defaultConfig {
        applicationId "com.example.xxxxx"
        minSdkVersion 14
        targetSdkVersion 19
    }

dexOptions {
        incremental true
        javaMaxHeapSize "4g"
     }
}

*******************************************************************

Method Two:

Add these two lines of code in manifest file...

android:hardwareAccelerated="false" 
android:largeHeap="true"

Example:

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:hardwareAccelerated="false"
        android:largeHeap="true"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

It will Work for sure any of these cases.....

4

解决方案:在您的清单文件中尝试以下内容,并使用Glide库

compile 'com.github.bumptech.glide:glide:3.7.0'

    **Use Glide Library and Override size to less size;**

 if (!TextUtils.isEmpty(message.getPicture())) {
            Glide.with(mContext).load(message.getPicture())
                    .thumbnail(0.5f)
                    .crossFade()
                    .transform(new CircleTransform(mContext))
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .into(holder.imgProfile);
        } 

android:hardwareAccelerated="false"

安卓:硬件加速 = "false"

android:largeHeap="true"

安卓:大内存 = "true"

<application
    android:allowBackup="true"
    android:hardwareAccelerated="false"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:largeHeap="true"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

使用此库

  compile 'com.github.bumptech.glide:glide:3.7.0'   

它正在工作,愉快的编码。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;

import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;


public class CircleTransform extends BitmapTransformation {
    public CircleTransform(Context context) {
        super(context);
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return circleCrop(pool, toTransform);
    }

    private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;

        int size = Math.min(source.getWidth(), source.getHeight());
        int x = (source.getWidth() - size) / 2;
        int y = (source.getHeight() - size) / 2;

        // TODO this could be acquired from the pool too
        Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);

        Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        float r = size / 2f;
        canvas.drawCircle(r, r, r, paint);
        return result;
    }

    @Override
    public String getId() {
        return getClass().getName();
    }
}

4

您的应用程序因图像大小(以 MB 或 KB 为单位)过大而崩溃,因此不会为其分配空间。因此,在将图像粘贴到可绘制对象之前,请先缩小其大小。

或者

您可以在 Manifest.xml 文件中的 application 标签中添加以下内容:

 android:hardwareAccelerated="false"
    android:largeHeap="true"
    android:allowBackup="true"

添加此应用程序后,将不会崩溃。

  • 始终在应用程序中使用较小的图像尺寸。
  • 如果您在应用程序中添加大尺寸图像,则应添加上述语法,但是应用程序大小将增加。

4

我也遇到了同样的问题。

和上面大多数人一样,这个问题是由过大的图像引起的。

只要调整一些图像大小,无需更改任何代码。


4
将android:largeHeap="true"添加到您应用程序清单的应用程序部分中?

1
还有另一种解决方案:将高度=高度0.75和宽度=宽度0.75。 - SAURABH OMER

3

添加后,我的问题得到了解决。

 dexOptions {
        incremental true
        javaMaxHeapSize "4g"
        preDexLibraries true
        dexInProcess = true
    }

在Build.Gradle文件中


1
这只会使你的应用程序在内存使用方面变得非常臃肿。 - OneCricketeer
1
@cricket_007 如果有的话,那会有什么后果? - Sreekanth Karumanaghat

3

我不是很建议像这样编辑清单

android:hardwareAccelerated="false",android:largeHeap="true"

这些选项会导致您的应用程序动画效果不流畅。此外,'liveData' 或更改本地数据库(Sqlite、Room)会激活缓慢。这对用户体验不好。

因此,我建议调整大小的位图

以下是示例代码

fun resizeBitmap(source: Bitmap): Bitmap {
      val maxResolution = 1000    //edit 'maxResolution' to fit your need
      val width = source.width
      val height = source.height
      var newWidth = width
      var newHeight = height
      val rate: Float

            if (width > height) {
                if (maxResolution < width) {
                    rate = maxResolution / width.toFloat()
                    newHeight = (height * rate).toInt()
                    newWidth = maxResolution
                }
            } else {
                if (maxResolution < height) {
                    rate = maxResolution / height.toFloat()
                    newWidth = (width * rate).toInt()
                    newHeight = maxResolution
                }
            }
            return Bitmap.createScaledBitmap(source, newWidth, newHeight, true)
 }

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