I have small Android test application where enabling largeHeap
eventually causes Out of Memory Error because garbage collection never gets triggered.
This is the code:
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
is JPEG image 2560x1707
pixels
When device is rotated image is reloaded. With largeHeap
enabled GC
never gets triggered and sequence of orientation changes will eventually result in OOM
. That does not happen with disabled largeHeap
. Also calling System.gc()
after rotation resolves the issue.
Memory consumption with largeHeap
enabled
Memory consumption with largeHeap
enabled and System.gc()
call
Memory consumption with largeHeap
disabled
I am able to reproduce this issue on Samsung SM-T210 API 19
device. Same type of device with API 16
works fine, as well as some other devices with API 19
like Samsung GT-N7100
and Asus K01A
. Obviously it is some kind of bug that happens only on specific API/device combinations.
Questions are:
- Is there anything inherently wrong with my code
- Is there some other (better) way for resolving the issue other than calling
System.gc()
I will try to address this issue in case of Orientation Change only, Complete OOM Exception workaround is beyond the scope of this answer:
You can perform recycling of images in
ImageView
inonDestroy()
because when the Orientation changes, activity'sonDestroy()
is called.You have to differentiate whether the
onDestroy()
is called because of orientation change or not to do this, you should use is callisFinishing()
Following is a snippet to demonstrate this:
Using
WeakReference
will help in addition to this, when creating theBitmap
object** EDIT **
I know this won't make much difference, but for me it does. The only option android has left for us to save memory, is to reduce Image quality along with other methods like using LruCache.
You could add another line before
options.inSampleSize
to reduce image's quality to save some memory.