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

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

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我在这一行开始这个错误(“电视”是一个textView):

 tv.setBackgroundResource(R.drawable.yanlis); 

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

您不能dynamic增加堆大小,但您可以请求使用更多。

机器人: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堆来创build。 这适用于为应用程序创build的所有进程。 它仅适用于加载到进程中的第一个应用程序; 如果您使用共享用户标识来允许多个应用程序使用某个进程,则他们都必须始终使用此选项,否则将会产生不可预知的结果。 大多数应用程序不应该需要这个,而应该专注于减less整体内存使用量,以提高性能。 启用此function也不能保证可用内存的固定增加,因为某些设备受到其可用内存总量的限制。


要在运行时查询可用的内存大小,请使用方法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等。

让我们更深入的图像采样:

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

为了告诉解码器对图像进行二次采样,将较小的版本加载到内存中,请在BitmapFactory.Options对象中将inSampleSize设置为true 。 例如,分辨率为2100 x 1500像素,使用inSampleSize为4解码的inSampleSize会生成大约512×384的位图。 加载到内存使用0.75MB,而不是12MB的完整图像(假设位图configurationARGB_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文档中的四舍五入取两个最接近的幂来计算两个值的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); } 

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

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

您可以按照类似的过程来解码来自其他来源的位图,方法是根据需要replace相应的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; } 

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


这不是一个好主意,使用 android:largeHeap="true"这是从谷歌提取解释它,

但是,请求大堆的function仅适用于一小组可以certificate需要消耗更多RAM的应用程序(如大型照片编辑应用程序)。 永远不要因为内存不足而请求一个大的堆,而只需要知道所有内存在哪里被分配,以及为什么必须保留这些内存,就应该使用它。 然而,即使你确信你的应用程序可以certificate这个大堆,你应该尽可能避免要求它。 使用额外内存将越来越不利于整体用户体验,因为垃圾收集将花费更长时间,并且在任务切换或执行其他常见操作时,系统性能可能会变慢。

out of memory errors不足out of memory errors我会说join这个清单以避免OOM问题不是一个罪


在Android运行时validation应用程序行为(ART)

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

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


解决垃圾收集(GC)问题

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

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


防止JNI问题

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


此外,您可以使用本机内存( NDK和JNI ),所以您实际上绕过了堆大小的限制。

这里有一些关于它的post:

这里有一个库:

处理位图时,应该实现一个LRUcachingpipe理器

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,让你configurationGlide模块和LRUCache

https://github.com/bumptech/glide

我只看到两个select:

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