I’m having trouble with my application running into out of memory errors. I’m fairly certain I’m leaking memory somewhere, and I believe I have narrowed it down to a particular Activity and believe the leak is linked to AdMob.
To illustrate What I am seeing, If launch my app, the first activity takes up around 3Mb (it displays an image). When the second activity is loaded, the first isn’t destroyed, and the total heap usage increases to ~ 7.8Mb. Once an AdMob ad has loaded in the second activity, the total heap size goes to about 12.5 Mb.
If I go back to the first activity by pressing the back button, the second activity’s onDestroy() method is called. However, the amount of memory used by my application does not decrease at all. Even If I call System.gc() explicitly. I may be wrong, but shouldn’t the amount of heap go back to 3Mb?
What is confusing me even more is that If I keep the app alive and open the second activity again, the heap size only increases maybe by 500kb or so each time I go back and fourth. It’s as if something is being kept alive and reused in the second activity even though the activity has been destroyed.
I’ve simplified my code here to see if someone can tell me what I am doing wrong. I have also had a look at the dump files using MAT but I’m not too sure on what I’m looking at and haven’t found much of use.
My first(default) activity
public class FirstActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.main);
}
public void startSelectionPage(View v){
Intent intent = new Intent(FirstActivity.this, ImageSelectActivity.class);
startActivity(intent);
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/startpage" >
<ImageButton
android:id="@+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/playbutton"
android:onClick="startSelectionPage"
/>
</RelativeLayout>
ImageSelectActivity
public class ImageSelectActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.selectionpage);
((Gallery) findViewById(R.id.gallery))
.setAdapter(new ImageAdapter(this.getApplicationContext(), 150));
}
@Override
public void onDestroy() {
AdView ad = (AdView) findViewById(R.id.adView);
ad.destroy();
super.onDestroy();
}
}
class ImageAdapter extends BaseAdapter {
private Context myContext;
private int imageBackground;
private int galleryHeight;
private int[] myImageIds = {
R.drawable.canyonthumb,
R.drawable.yosemitethumb,
R.drawable.flowerthumb,
R.drawable.squirrelthumb
};
public ImageAdapter(Context c, int galleryHeight) {
this.myContext = c;
TypedArray ta = c.obtainStyledAttributes(R.styleable.GalleryTheme);
imageBackground = ta.getResourceId(R.styleable.GalleryTheme_android_galleryItemBackground, 1);
ta.recycle();
this.galleryHeight = galleryHeight;
}
public int getCount() { return this.myImageIds.length; }
public Object getItem(int position) { return position; }
public long getItemId(int position) { return position; }
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(myContext);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setLayoutParams(new Gallery.LayoutParams(galleryHeight,galleryHeight));
imageView.setBackgroundResource(imageBackground);
imageView.setImageResource(this.myImageIds[position]);
return imageView;
}
}
selectionpage.xml
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/chooseanimage"/>
<RelativeLayout
android:id="@+id/rlayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<com.google.ads.AdView android:id="@+id/adView"
android:layout_width="wrap_content"
android:layout_height="60dp"
ads:adUnitId="----"
ads:adSize="IAB_BANNER"
ads:testDevices="TEST_EMULATOR,---"
ads:loadAdOnCreate="true"
/>
<Gallery
android:id="@+id/gallery"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/imageView1"
android:layout_above="@+id/adView"
/>
</RelativeLayout>
</LinearLayout>
I do not believe I ever had a memory leak. The issue was with the GC, which seemingly allowed objects to remain in memory for much longer that I expected. If I try the code on a 16Mb emulator, the GC works much more aggressively.
Another thing I did not expect was the high memory usage of AdMob. Adding adverts to one of my activities has resulted in ~8MB of heap being used.