如何在Android中解决java.lang.OutOfMemoryError问题

问题描述 投票:63回答:5

虽然我在drawable文件夹中的图像非常小,但我从用户那里得到了这个错误。我没有在代码中使用任何位图功能。至少故意:)

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);

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

android bitmap out-of-memory bitmapfactory setbackground
5个回答
130
投票

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

机器人:largeHeap = “真”

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);

If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.

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

Lets get more deeper to Image Sampling:

例如,如果将最终显示在ImageView中的128x128像素缩略图中,则不值得将1024x768像素图像加载到内存中。

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

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;
}

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

要使用此方法,首先使用设置为inJustDecodeBoundstrue进行解码,传递选项,然后使用新的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);
}

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

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;
}

How to Manage Your App's Memory: link


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

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

在与out of memory errors激动地工作之后,我会说将此添加到清单中以避免问题不是罪


Verifying App Behavior on the Android Runtime (ART)

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

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


Addressing Garbage Collection (GC) Issues

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

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


Preventing JNI Issues

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


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

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

这是一个为它制作的图书馆:


3
投票

我只看到两个选项:

  1. 您的应用程序中存在内存泄漏。
  2. 运行应用程序时,设备没有足够的内存。

3
投票

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

http://developer.android.com/reference/android/util/LruCache.html http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html When should I recycle a bitmap using LRUCache?

要么

使用像Universal Image Loader这样的层库:

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

编辑:

现在处理图像时大部分时间都使用位图我使用Glide来配置Glide模块和LRUCache

https://github.com/bumptech/glide


1
投票

为Android应用程序处理此类错误/异常的提示很少:

  1. 活动和应用程序有以下方法: onLowMemory onTrimMemory处理这些方法以监视内存使用情况。
  2. Manifest中的tag可以将属性'largeHeap'设置为TRUE,这会为App sandbox请求更多堆。
  3. 管理内存缓存和磁盘缓存: 应用程序运行时,图像和其他数据可能已缓存在内存中(本地活动/片段和全局);应该管理或删除。
  4. 使用WeakReference,Java实例创建的SoftReference,特别是文件。
  5. 如果有这么多图像,请使用适当的库/数据结构来管理内存,使用加载的图像,处理磁盘缓存。
  6. 处理OutOfMemory异常
  7. 遵循编码的最佳实践 内存泄漏(不要拿着强大的参考资料)
  8. 最小化活动堆栈,例如堆栈中的活动数量(不要在上下文/活动中保存所有内容) 上下文是有道理的,那些不需要超出范围(活动和片段)的数据/实例将它们保存到适当的上下文而不是全局引用保持。
  9. 最大限度地减少静力学的使用,更多的单身人士。
  10. 照顾OS基本内存基础 内存碎片问题
  11. 当您确定不再需要内存中缓存时,有时会手动调用GC.Collect()。

1
投票

如果你得到这个错误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" />
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.