AdView导致内存泄漏

时间:2015-11-03 21:26:47

标签: android memory-leaks admob adview

我正在使用AdView和LeakCanary。托管adView的片段在onDestroy中调用adView.destroy(),但LeakCanary显示托管此片段的Activity被com.google.android.gms.common.api.a.a.a.i泄露 。堆转储还显示存在内存泄漏。当我删除AdView.loadAd()并且不在片段中加载广告时 - 没有泄漏。有什么想法或建议吗?谢谢。

5 个答案:

答案 0 :(得分:10)

同样的问题,AdView有一个内部变量(强引用)保留在上下文中,这是我的Activity,导致Activity实例泄露。

我的依赖是com.google.android.gms:play-services-ads:8.3.0

解决方法是在创建AdView实例时提供应用程序上下文。

AdView adview = new AdView(getApplicationContext());

答案 1 :(得分:2)

您可以尝试以下方法:

  • onDestroyView()
  • 中移动你的逻辑
  • 首先从容器中删除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;
}

1。在onDestroyView

中破坏Adview
  • 片段生命周期具有方法onDestroyView,该视图在销毁视图时会调用,因此您应该在此位置完全销毁AdView。
  • 在您的情况下,您正在onDestroy onDestroyView之后)中销毁AdView,所以这是一个泄漏。因为Fragment View被销毁后,AdView仍然存在。
  • 活动没有onDestroyView方法,视图在“活动”的onDestroy中被破坏。因此,我们清除了onDestroy中的对象。

2。通过编程从视图中删除AdView。

  

但是,如果我们在XML中使用adView,则无法做到这一点。

因为您要从XML扩展AdView,所以在onDestroy中删除View会为您完成这项工作。

3。在onDestroyView

中将AdView设为NULL

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:

  1. 从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>
    
  2. 然后在您的活动中的mAdView旁边定义一个RelativeLayout变量(adscontainer):

       private AdView mAdView;
       private RelativeLayout adscontainer; 
    
  3. 在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选项的横幅。

  4. 在onDestroy()中,添加以下内容:

       if (mAdView != null){
            mAdView.setAdListener(null);
            adscontainer.removeAllViews();
            adscontainer = null;
            mAdView.destroy(); 
            mAdView = null;               
           }
    
  5. 现在,让我们谈谈“ MainActivity.MemoryLeakContainerActivity”: 是的,您必须牺牲自己的一项活动来控制内存泄漏。我没有找到其他方法。我在这里尝试了“ getApplicationContext()”,但没有成功。因此,我选择MainActivity的原因有两个:

    a-在我的应用程序中,MainActivity是一个简单的菜单,带有三个按钮(“播放”,“查看平台”和“创建平台”)。它并不会消耗太多内存,也无需销毁它,因为我应用程序中的大多数onBackPressed()任务都会将流程引导至菜单,并且无论如何它将永远存在于内存中。

    b-这是第一个要加载的Activity。因此,广告视图将随时可用,并可以用于其他活动。

  6. 在MainActivity(或您选择包含内存泄漏的活动)中,在底部添加以下内容:

     public static MainActivity MemoryLeakContainerActivity;
     public MainActivity() {
         super();
         if (MemoryLeakContainerActivity != null) {
         throw new IllegalStateException("MemoryLeakContainerActivity is already created");
         }
         MemoryLeakContainerActivity = this;
     }
    
  7. 就是这样!我的应用程序中没有更多的内存泄漏! LeakCanary报告很干净。

注意:这是替代方法,而不是解决方案。解决方案应仅来自GOOGLE:一个简单的命令(例如“ mAdView.destroy()”)即可完成工作,对吧?

此问题正在Google论坛中讨论:https://groups.google.com/forum/#!topic/google-admob-ads-sdk/9IyjqdmeumM