启用largeHeap时,应用程序中未触发垃圾收集,从而导致OOM

我有一个小的Android测试应用程序,其中启用largeHeap最终会导致Out of Memory Error,因为垃圾收集永远不会被触发。

这是代码:

MainActivity.java

 package com.example.oomtest; import android.app.Activity; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.util.DisplayMetrics; import android.widget.ImageView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels; int height = dm.heightPixels; ImageView iv = (ImageView) findViewById(R.id.background_image); iv.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.drawable.g01, width, height)); // System.gc(); } public static int calculateInSampleSize(int width, int height, int reqWidth, int reqHeight) { int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; } public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight); options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } } 

activity_main.xml中

    

AndroidManifest.xml中

            

drawable.g01是JPEG图像2560x1707像素

当设备旋转时,重新加载图像。 启用largeHeap GC永远不会被触发,方向更改序列最终会导致OOM 。 禁用的largeHeap不会发生这种情况。 在轮换后调用System.gc()也可以解决问题。

启用largeHeap内存消耗 启用largeHeap时的内存消耗

启用largeHeapSystem.gc()调用的内存消耗 启用largeHeap和System.gc()调用的内存消耗

禁用largeHeap内存消耗 禁用largeHeap时的内存消耗

我可以在Samsung SM-T210 API 19设备上重现此问题。 具有API 16相同types的设备工作正常,以及具有API 19一些其他设备,如Samsung GT-N7100Asus K01A 。 显然,它只是在特定的API /设备组合上发生的某种错误。

问题是:

  1. 我的代码有什么本质上的错误
  2. 除了调用System.gc()之外,还有其他(更好的)解决问题的方法吗?

我将尝试仅在Orientation Change的情况下解决此问题,Complete OOM Exception解决方法超出了此答案的范围:

您可以在onDestroy()中的ImageView中执行图像的回收,因为当Orientation更改时,将调用activity的onDestroy()

你必须区分onDestroy()是否因为方向改变而被调用,你应该使用is call isFinishing()

以下是一个演示以下内容的片段:

 ImageView iv; // globally defined in class @Override protected void onDestroy(){ super.onDestroy(); if (isFinishing()) { // don't do anything activity is destroying because of other reasons } else{ // activity is being destroyed because of orientation change Drawable drawable = iv.getDrawable(); if (drawable instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; Bitmap bitmap = bitmapDrawable.getBitmap(); bitmap.recycle(); iv.setImageBitmap(null); // edited } } } 

在创建Bitmap对象时,使用WeakReference将有助于此

 WeakReference bm; //initialize it however you want 

** 编辑 **

我知道这不会有太大的区别,但对我来说确实如此。 android为我们节省内存的唯一选择是降低图像质量以及使用LruCache等其他方法。

您可以在options.inSampleSize之前添加另一行,以降低图像的质量以节省一些内存。

 options.inPreferredConfig = Config.RGB_565;