我正在使用AdView和LeakCanary。托管adView的片段在onDestroy中调用adView.destroy(),但LeakCanary显示托管此片段的Activity被com.google.android.gms.common.api.a.a.a.i泄露 。堆转储还显示存在内存泄漏。当我删除AdView.loadAd()并且不在片段中加载广告时 - 没有泄漏。有什么想法或建议吗?谢谢。
答案 0 :(得分:10)
同样的问题,AdView有一个内部变量(强引用)保留在上下文中,这是我的Activity,导致Activity实例泄露。
我的依赖是com.google.android.gms:play-services-ads:8.3.0
解决方法是在创建AdView实例时提供应用程序上下文。
AdView adview = new AdView(getApplicationContext());
答案 1 :(得分:2)
您可以尝试以下方法:
首先从容器中删除adView
,然后调用destroy(),即
ViewParent parent = adView.getParent();
if (parent != null && parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(adView);
}
adView.destroy();
adView = null;
答案 2 :(得分:0)
我认为将应用上下文传递给AdView并不是真正的解决方案。因为问题是AdView没有释放Context Object。因此,如果通过它,它将不会释放应用上下文。
因此,下面是一种可以真正防止泄漏的解决方法。
@Override
protected void onDestroyView() {
super.onDestroy();
if (adview != null && adview.getParent() != null) // inflated by XML and remove here from parent
((ViewGroup) adview.getParent()).removeView(adview);
adview.destroy();
adview = null;
}
onDestroyView
onDestroyView
,该视图在销毁视图时会调用,因此您应该在此位置完全销毁AdView。onDestroy
( onDestroyView之后)中销毁AdView,所以这是一个泄漏。因为Fragment View被销毁后,AdView仍然存在。onDestroyView
方法,视图在“活动”的onDestroy
中被破坏。因此,我们清除了onDestroy
中的对象。但是,如果我们在XML中使用adView,则无法做到这一点。
因为您要从XML扩展AdView,所以在onDestroy
中删除View会为您完成这项工作。
onDestroyView
在null
中制作AdView onDestroy
。因此,将不再引用AdView对象。它将由垃圾收集器清理。
我希望这些信息对您有用。 :)
答案 3 :(得分:0)
就我而言,这是由于在Lambda范围内将MobileAds初始化代码与this
一起使用所引起的。将this
更改为applicationContext
后,此问题已解决。
之前:
MobileAds.initialize(this, "ca-app-pub-0000000000000000~0000000000")
之后:
MobileAds.initialize(applicationContext, "ca-app-pub-0000000000000000~0000000000")
答案 4 :(得分:-1)
我尝试了stackoverflow论坛和所有其他Google论坛中讨论的所有解决方案。内存泄漏似乎是不稳定的:如果用户在显示广告之前离开活动,则出现内存泄漏的机会似乎更高。
对我来说唯一有用的是将内存泄漏控制在一个活动中...
第一个建议: 不要将adview直接添加到XML布局文件中。如果您遵循官方文档(https://developers.google.com/admob/android/banner)中的说明,那肯定会导致内存泄漏。 而是以编程方式添加adview:
从XML文件中删除adview:
<RelativeLayout
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:id="@+id/RLadViewContainer"
android:layout_width="match_parent"
android:layout_height="50dp"
>
<com.google.android.gms.ads.AdView <<< REMOVE IT
android:id="@+id/adView" <<< REMOVE IT
android:layout_width="wrap_content" <<< REMOVE IT
android:layout_height="wrap_content" <<< REMOVE IT
android:layout_centerHorizontal="true" <<< REMOVE IT
android:layout_alignParentBottom="true" <<< REMOVE IT
ads:adSize="BANNER" <<< REMOVE IT
ads:adUnitId="@string/banner_ad_unit_id"> <<< REMOVE IT
</com.google.android.gms.ads.AdView> <<< REMOVE IT
</RelativeLayout>
然后在您的活动中的mAdView旁边定义一个RelativeLayout变量(adscontainer):
private AdView mAdView;
private RelativeLayout adscontainer;
在OnCreate中,删除旧的mAdView分配并将其替换为以下内容:
adscontainer = findViewById(R.id.RLadViewContainer);
mAdView = new AdView(MainActivity.MemoryLeakContainerActivity);//THIS IS THE TRICK ;)
mAdView.setAdSize(AdSize.BANNER);
mAdView.setAdUnitId(getResources().getString(R.string.banner_ad_unit_id));
adscontainer.addView(mAdView);
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mAdView.getLayoutParams();
lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
mAdView.setLayoutParams(lp);
RequestConfiguration requestConfiguration = new RequestConfiguration.Builder()
.setTestDeviceIds(Constants.testDevices)
.build();
MobileAds.setRequestConfiguration(requestConfiguration);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest); //Move this line to the right place, wherever you need.
注意:这应该在底部显示带有CENTER_HORIZONTAL和ALIGN_PARENT_BOTTOM选项的横幅。
在onDestroy()中,添加以下内容:
if (mAdView != null){
mAdView.setAdListener(null);
adscontainer.removeAllViews();
adscontainer = null;
mAdView.destroy();
mAdView = null;
}
现在,让我们谈谈“ MainActivity.MemoryLeakContainerActivity”: 是的,您必须牺牲自己的一项活动来控制内存泄漏。我没有找到其他方法。我在这里尝试了“ getApplicationContext()”,但没有成功。因此,我选择MainActivity的原因有两个:
a-在我的应用程序中,MainActivity是一个简单的菜单,带有三个按钮(“播放”,“查看平台”和“创建平台”)。它并不会消耗太多内存,也无需销毁它,因为我应用程序中的大多数onBackPressed()任务都会将流程引导至菜单,并且无论如何它将永远存在于内存中。
b-这是第一个要加载的Activity。因此,广告视图将随时可用,并可以用于其他活动。
在MainActivity(或您选择包含内存泄漏的活动)中,在底部添加以下内容:
public static MainActivity MemoryLeakContainerActivity;
public MainActivity() {
super();
if (MemoryLeakContainerActivity != null) {
throw new IllegalStateException("MemoryLeakContainerActivity is already created");
}
MemoryLeakContainerActivity = this;
}
就是这样!我的应用程序中没有更多的内存泄漏! LeakCanary报告很干净。
注意:这是替代方法,而不是解决方案。解决方案应仅来自GOOGLE:一个简单的命令(例如“ mAdView.destroy()”)即可完成工作,对吧?
此问题正在Google论坛中讨论:https://groups.google.com/forum/#!topic/google-admob-ads-sdk/9IyjqdmeumM