我的应用程序遇到内存不足的错误。我相当确定我在某个地方泄露了内存,我相信我已经将其缩小到一个特定的活动,并相信泄漏与AdMob有关。
为了说明我所看到的,如果启动我的应用程序,第一个活动占用大约3Mb(它显示一个图像)。当加载第二个活动时,第一个活动不会被销毁,堆的总使用量增加到~ 7.8Mb。一旦AdMob广告在第二个活动中加载,总堆大小将达到约12.5 Mb。
如果我通过按后退按钮返回到第一个活动,则调用第二个活动的onDestroy()
方法。但是,我的应用程序使用的内存量根本没有减少。即使我显式调用System.gc()。我可能错了,但是堆的大小不应该回到3Mb吗?
更让我困惑的是,如果我保持应用程序存活并再次打开第二个活动,堆大小只会增加500kb左右,每次我回到第四个。这就好像某些东西在第二个活动中保持活动并被重用,即使该活动已被销毁。
我在这里简化了我的代码,看看是否有人能告诉我我做错了什么。我也用MAT看了看转储文件,但我不太确定我在看什么,也没有发现太多的用途。
我的第一个(默认)活动
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>
查看这个问题:activity生命周期中的android内存管理。这与你的问题不完全相同,但我相信答案中包含了你想要的信息。
因此,与其冗长地解释内存、垃圾收集器和活动生命周期之间的关系,不如让您参考上面的问题
我不确定我是否得到了正确的代码,但这里是我的想法:
在ImageSelectActivity
中,您创建了ImageAdapter
的新对象。当您在setAdapter()
调用中使用new
时,它是一个匿名对象。
只要你的活动存在,ImageAdapter
对象就不会被销毁。您可以在onDestroy()
一篇非常好的博客文章是关于你的主题:避免内存泄漏
结尾第三点:
如果你不控制,避免在activity中使用非静态的内部类它们的生命周期,使用一个静态内部类并做一个弱引用到里面的活动。这个问题的解决方案是使用静态使用WeakReference指向外部类的内部类,如viewwroot及其W内部类
我不相信我曾经有过内存泄漏。问题在于GC,它似乎允许对象在内存中保留的时间比我预期的要长得多。如果我在一个16Mb的模拟器上尝试这个代码,GC的工作效率会高得多。
我没有想到的另一件事是AdMob的高内存使用率。在我的一个活动中添加广告导致使用了约8MB的堆。