我是一名有用的助手,可以为您进行翻译。
我有一个小型的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
是一张尺寸为 2560x1707
像素的 JPEG 图片。
当设备旋转时,图片会被重新加载。启用 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()
,是否还有其他更好的方法来解决此问题?
onDestroy()
和recycle()
只能稍微减缓内存消耗的上升速度。 - Dalija PrasnikaronConfigurationChanged
,但这并不是问题的关键。这只是在创建MCVE时从真实应用程序中复制代码的副作用。我已经编辑了包含简化代码的问题。 - Dalija Prasnikar