我有一个小型的Android测试应用程序,其中启用largeHeap
最终会导致内存不足错误,因为永远不会触发垃圾回收。
这是代码:
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
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/background_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
</RelativeLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.oomtest" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:largeHeap="true"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
drawable.g01
是JPEG图像2560x1707
像素旋转设备时,将重新加载图像。启用
largeHeap
后,GC
将永远不会触发,并且方向变化的顺序最终将导致OOM
。禁用largeHeap
不会发生这种情况。轮换后也调用System.gc()
可解决此问题。启用
largeHeap
的内存消耗启用
largeHeap
和System.gc()
调用后的内存消耗禁用
largeHeap
的内存消耗我可以在
Samsung SM-T210 API 19
设备上重现此问题。具有API 16
的相同类型的设备以及具有API 19
的某些其他设备(例如Samsung GT-N7100
和Asus K01A
)都可以正常工作。显然,这是仅在特定的API/设备组合上才会发生的某种错误。问题是:
System.gc()
最佳答案
我将尝试仅在方向更改的情况下解决此问题,完整的OOM异常解决方法不在此答案的范围内:
您可以在ImageView
中的onDestroy()
中执行图像的回收,因为当方向更改时,将调用 Activity 的onDestroy()
。
您必须区分是否由于方向改变而调用了onDestroy()
,为此,您应该使用的是调用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
}
}
}
在创建
WeakReference
对象时,除了使用Bitmap
外,它还可以提供帮助。WeakReference<Bitmap> bm; //initialize it however you want
** 编辑 **
我知道这不会有太大的不同,但对我而言确实如此。 android留给我们节省内存的唯一选择是降低图像质量以及其他方法,例如使用LruCache。
您可以在
options.inSampleSize
之前添加另一行以降低图像质量以节省一些内存。options.inPreferredConfig = Config.RGB_565;
关于java - 启用largeHeap导致OOM时,应用程序中未触发垃圾回收,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30776779/