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

时间:2014-09-08 07:53:06

标签: android bitmap out-of-memory bitmapfactory setbackground

Altough我在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);

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

7 个答案:

答案 0 :(得分:127)

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

  

机器人: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等。

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

例如,如果将最终显示在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;
}
  

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

要使用此方法,首先将inJustDecodeBounds设置为true进行解码,传递选项,然后使用新的inSampleSize值再次解码,inJustDecodeBounds设置为{ {1}}:

false

此方法可以轻松地将任意大尺寸的位图加载到显示100x100像素缩略图的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

您可以按照类似的过程解码来自其他来源的位图,方法是根据需要替换相应的mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100)); 方法。


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

BitmapFactory.decode*

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


使用 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; } 这不是一个好主意,谷歌的摘录解释了它,

  

但是,请求大堆的功能仅适用于a   一小组应用程序,可以证明需要消耗更多的RAM(例如   作为一个大型照片编辑应用程序)。永远不要简单地请求大堆   因为你已经没有内存而你需要快速修复 - 你应该这样做   只有当你确切知道所有记忆的存在时才使用它   分配以及必须保留的原因。然而,即使你有信心   你的应用程序可以证明大堆的合理性,你应该避免请求它   尽可能的。使用额外的内存将越来越多   因为垃圾而损害整体用户体验   收集将花费更长时间,系统性能可能会更慢   任务切换或执行其他常见操作。

在与android:largeHeap="true"一起工作之后,我会说将这个添加到清单中以避免问题不是罪恶


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

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

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


解决垃圾收集(GC)问题

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

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


防止JNI问题

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


此外,您可以使用本机内存(NDK&amp; JNI),因此实际上可以绕过堆大小限制。

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

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

答案 1 :(得分:3)

我只看到两个选项:

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

答案 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?

OR

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

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

编辑:

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

https://github.com/bumptech/glide

答案 3 :(得分:1)

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

  1. 活动&amp;应用程序具有以下方法:

    • onLowMemory
    • onTrimMemory 处理这些方法以观察内存使用情况。
  2. Manifest中的
  3. 标签可以具有属性&#39; largeHeap&#39;设置为TRUE,为App沙箱请求更多堆。

  4. 管理内存缓存&amp;磁盘缓存:

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

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

  7. 处理OutOfMemory异常

  8. 遵循编码的最佳做法

    • 记忆力泄漏(不要强力推荐所有东西)
  9. 最小化活动堆栈,例如堆栈中的活动数量(不要在上下文/活动中保存所有内容)

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

  11. 照顾OS基本内存基础

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

答案 4 :(得分: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" />

答案 5 :(得分:0)

android:largeHeap="true"无法解决错误

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

android:width="24dp"
android:height="24dp"
android:viewportWidth="3033"
android:viewportHeight="3033"

答案 6 :(得分:0)

检查图像大小

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

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

<块引用>

课程:缩小图片尺寸/延迟图片加载