74

尽管我在可绘制文件夹中有非常小的图像,但我从用户那里收到了这个错误。而且我没有在代码中使用任何位图函数。至少是故意的:)

java.lang.OutOfMemoryError
    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:683)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:513)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:889)
    at android.content.res.Resources.loadDrawable(Resources.java:3436)
    at android.content.res.Resources.getDrawable(Resources.java:1909)
    at android.view.View.setBackgroundResource(View.java:16251)
    at com.autkusoytas.bilbakalim.SoruEkrani.cevapSecimi(SoruEkrani.java:666)
    at com.autkusoytas.bilbakalim.SoruEkrani$9$1.run(SoruEkrani.java:862)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:146)
    at android.app.ActivityThread.main(ActivityThread.java:5602)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
    at dalvik.system.NativeStart.main(Native Method)

根据这个stackTrace,我在这一行遇到了这个错误('tv'是一个textView):

tv.setBackgroundResource(R.drawable.yanlis);

问题是什么?如果您需要有关代码的其他信息,我可以添加。谢谢!

4

7 回答 7

152

您不能动态增加堆大小,但您可以通过 using 请求使用更多。

机器人:大堆=“真”

在中manifest.xml,您可以在清单中添加这些行,它适用于某些情况。

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

是否应使用大型 Dalvik 堆创建应用程序的进程。这适用于为应用程序创建的所有进程。它仅适用于加载到进程中的第一个应用程序;如果您使用共享用户 ID 来允许多个应用程序使用一个进程,则它们都必须一致地使用此选项,否则它们将产生不可预知的结果。大多数应用程序不应该需要这个,而是应该专注于减少它们的整体内存使用以提高性能。启用此功能也不能保证可用内存的固定增加,因为某些设备受到其总可用内存的限制。


要在运行时查询可用内存大小,请使用方法getMemoryClass()getLargeMemoryClass()

如果仍然面临问题,那么这也应该有效

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inSampleSize = 8;
 mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);

如果设置为大于 1 的值,则请求解码器对原始图像进行二次采样,返回较小的图像以节省内存。

这是 BitmapFactory.Options.inSampleSize 在显示图像速度方面的最佳使用。文档提到使用 2 的幂的值,所以我正在使用 2、4、8、16 等。

让我们更深入地了解图像采样:

例如,如果 1024x768 像素的图像最终会以ImageView.

告诉解码器对图像进行二次采样,将较小的版本加载到内存中,在您的对象中设置inSampleSize为。例如,分辨率为 2100 x 1500 像素的图像使用4 进行解码会生成大约 512x384 的位图。将其加载到内存中使用 0.75MB 而不是 12MB 的完整图像(假设位图配置为)。这是一种根据目标宽度和高度计算样本大小值的方法,该值是 2 的幂:trueBitmapFactory.OptionsinSampleSizeARGB_8888

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

注意:根据 inSampleSize文档,计算二值的幂是因为解码器通过向下舍入到最接近的二的幂来使用最终值。

要使用此方法,首先使用inJustDecodeBounds设置为解码true,传递选项,然后使用新inSampleSize值再次解码并inJustDecodeBounds设置为false

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

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

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

此方法可以轻松地将任意大尺寸的位图加载到ImageView显示 100x100 像素缩略图的图像中,如以下示例代码所示:

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

您可以按照类似的过程来解码来自其他来源的位图,BitmapFactory.decode*方法是根据需要替换适当的方法。


我发现这段代码也很有趣:

private Bitmap getBitmap(String path) {

Uri uri = getImageUri(path);
InputStream in = null;
try {
    final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
    in = mContentResolver.openInputStream(uri);

    // Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, o);
    in.close();

    int scale = 1;
    while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) > 
          IMAGE_MAX_SIZE) {
       scale++;
    }
    Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth + ", 
       orig-height: " + o.outHeight);

    Bitmap bitmap = null;
    in = mContentResolver.openInputStream(uri);
    if (scale > 1) {
        scale--;
        // scale to max possible inSampleSize that still yields an image
        // larger than target
        o = new BitmapFactory.Options();
        o.inSampleSize = scale;
        bitmap = BitmapFactory.decodeStream(in, null, o);

        // resize to desired dimensions
        int height = bitmap.getHeight();
        int width = bitmap.getWidth();
        Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
           height: " + height);

        double y = Math.sqrt(IMAGE_MAX_SIZE
                / (((double) width) / height));
        double x = (y / height) * width;

        Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) x, 
           (int) y, true);
        bitmap.recycle();
        bitmap = scaledBitmap;

        System.gc();
    } else {
        bitmap = BitmapFactory.decodeStream(in);
    }
    in.close();

    Log.d(TAG, "bitmap size - width: " +bitmap.getWidth() + ", height: " + 
       bitmap.getHeight());
    return bitmap;
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
}

如何管理应用程序的内存:链接


android:largeHeap="true"在这里使用谷歌的摘录来解释它 不是一个好主意,

但是,请求大堆的能力仅适用于一小部分可以证明需要消耗更多 RAM 的应用程序(例如大型照片编辑应用程序)。永远不要仅仅因为你的内存已经用完并且需要快速修复就请求一个大堆——只有在你知道所有内存被分配到哪里以及为什么必须保留它时才应该使用它。然而,即使您确信您的应用程序可以证明大堆是合理的,您也应该尽可能避免请求它。使用额外的内存将越来越多地损害整体用户体验,因为垃圾收集将花费更长的时间,并且在任务切换或执行其他常见操作时系统性能可能会变慢。

在与我一起努力工作之后,out of memory errors我会说将其添加到清单中以避免 oom 问题不是一种罪过


在 Android 运行时 (ART) 上验证应用行为

Android 运行时 (ART) 是运行 Android 5.0(API 级别 21)及更高版本的设备的默认运行时。此运行时提供了许多可提高 Android 平台和应用程序的性能和流畅性的功能。您可以在介绍 ART中找到有关 ART 新功能的更多信息。

然而,一些适用于 Dalvik 的技术不适用于 ART。本文档让您了解在迁移现有应用程序以与 ART 兼容时需要注意的事项。大多数应用程序在使用 ART 运行时应该可以正常工作。


解决垃圾收集 (GC) 问题

在 Dalvik 下,应用程序经常发现显式调用 System.gc() 以提示垃圾回收 (GC) 很有用。这对于 ART 来说应该没那么必要了,尤其是当您调用垃圾收集来防止 GC_FOR_ALLOC 类型的出现或减少碎片时。您可以通过调用 System.getProperty("java.vm.version") 来验证正在使用的运行时。如果使用 ART,则属性值为“2.0.0”或更高。

此外,Android 开源项目 (AOSP) 中正在开发压缩垃圾收集器,以改进内存管理。因此,您应该避免使用与压缩 GC 不兼容的技术(例如保存指向对象实例数据的指针)。这对于使用 Java 本机接口 (JNI) 的应用程序尤其重要。有关更多信息,请参阅防止 JNI 问题。


防止 JNI 问题

ART 的 JNI 比 Dalvik 的要严格一些。使用 CheckJNI 模式来捕获常见问题是一个特别好的主意。如果您的应用使用 C/C++ 代码,您应该查看以下文章:


此外,您可以使用本机内存(NDKJNI),因此您实际上绕过了堆大小限制。

以下是一些关于它的帖子:

这是为它制作的图书馆:

于 2014-09-08T08:08:28.117 回答
6

我只看到两个选项:

  1. 您的应用程序中存在内存泄漏。
  2. 运行应用程序时设备没有足够的内存。
于 2014-09-08T07:57:38.597 回答
5

如果您收到此错误 java.lang.OutOfMemoryError,这是 Android 中最常见的问题。当由于内存空间不足而无法分配对象时,Java 虚拟机 (JVM) 会引发此错误。

android:hardwareAccelerated="false" , android:largeHeap="true"在应用程序下的 manifest.xml 文件中尝试此操作,如下所示:

<application
  android:name=".MyApplication"
  android:allowBackup="true"
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/AppTheme"
  android:hardwareAccelerated="false"
  android:largeHeap="true" />
于 2019-04-04T09:20:20.943 回答
4

处理位图时应该实现 LRU 缓存管理器

http://developer.android.com/reference/android/util/LruCache.html http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html 我什么时候应该使用 LRUCache 回收位图?

或者

使用 Universal Image Loader 之类的层库:

https://github.com/nostra13/Android-Universal-Image-Loader

编辑 :

现在在处理图像和大多数时间处理位图时,我使用 Glide,它可以让你配置 Glide Module 和 LRUCache

https://github.com/bumptech/glide

于 2014-09-08T08:00:43.603 回答
4

处理 Android 应用程序此类错误/异常的一些提示:

  1. 活动和应用程序具有以下方法:

    • 低内存
    • onTrimMemory 处理这些方法以监视内存使用情况。
  2. Manifest 中的标记可以将属性“largeHeap”设置为 TRUE,这会为 App 沙箱请求更多堆。

  3. 管理内存缓存和磁盘缓存:

    • 图像和其他数据可能在应用程序运行时缓存在内存中(本地在活动/片段中和全局中);应该管理或删除。
  4. 使用 Wea​​kReference,Java 实例创建的 SoftReference,特别是文件。

  5. 如果图像太多,请使用可以管理内存的适当库/数据结构,使用加载图像的采样,处理磁盘缓存。

  6. 处理 OutOfMemory 异常

  7. 遵循编码的最佳实践

    • 内存泄漏(不要用强参考持有所有东西)
  8. 最小化活动堆栈,例如堆栈中的活动数量(不要将所有内容都保存在上下文/活动中)

    • 上下文是有意义的,那些不需要超出范围(活动和片段)的数据/实例,将它们保存在适当的上下文中,而不是全局引用保存。
  9. 尽量减少使用静力学,更多的单例。

  10. 照顾操作系统的基本内存基础

    • 内存碎片问题
  11. 当您确定不再需要内存缓存时,有时手动调用 GC.Collect()。

于 2018-04-18T12:26:56.253 回答
0

android:largeHeap="true"没有修复错误

在我的情况下,我通过将 SVG 转换为矢量将图标/图像添加到 Drawable 文件夹后出现此错误。只需转到图标 xml 文件并为宽度和高度设置小数字

android:width="24dp"
android:height="24dp"
android:viewportWidth="3033"
android:viewportHeight="3033"
于 2020-07-20T15:23:08.160 回答
0

检查图像大小

我直接通过 XML ( app:srcCompat) 在 imageview 中加载了一个 ~350kB 的图像,这导致了 OOM 错误并且应用程序崩溃了。

为了解决这个问题,我使用 Glide 将完全相同的图像加载到同一个图像视图中,并且它起作用了!

课程:减小图像大小/延迟加载图像

于 2021-04-05T10:10:04.273 回答