Android AdView导致内存泄漏

Android AdView导致内存泄漏,android,memory-leaks,admob,adview,Android,Memory Leaks,Admob,Adview,我用的是AdView和LeakCanary。onDestroy中承载adView调用adView.destroy()的片段,但LeakCanary显示承载此片段的活动是由com.google.android.gms.common.api.a.a.i泄漏的 . 堆转储还表明存在内存泄漏。当我删除AdView.loadAd()并且不在片段中加载Ad时,就没有泄漏。有什么想法或建议吗?谢谢 您可以尝试以下方法吗: adscontainer = findViewById(R.id.RLadVi

我用的是AdView和LeakCanary。onDestroy中承载adView调用adView.destroy()的片段,但LeakCanary显示承载此片段的活动是由com.google.android.gms.common.api.a.a.i泄漏的
. 堆转储还表明存在内存泄漏。当我删除AdView.loadAd()并且不在片段中加载Ad时,就没有泄漏。有什么想法或建议吗?谢谢

您可以尝试以下方法吗:

     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.
   if (mAdView != null){
        mAdView.setAdListener(null);
        adscontainer.removeAllViews();
        adscontainer = null;
        mAdView.destroy(); 
        mAdView = null;               
       }
  • 把你的逻辑移开
  • 首先从容器中删除您的
    adView
    ,然后调用destroy(),即


同样的问题,AdView有一个内部变量(强引用)保存在上下文中,这就是我的活动,导致活动实例泄漏

AdView adview = new AdView(getApplicationContext());
我的依赖项是
com.google.android.gms:play-services-ads:8.3.0

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

AdView adview = new AdView(getApplicationContext());

我认为将App Context传递给AdView并不是一个真正的解决方案。因为问题是AdView并没有释放上下文对象。因此,如果您传递它,它将不会释放应用程序上下文

因此,下面可以作为一个解决办法,真正防止泄漏

@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中的AdView(onDestroyView之后的),因此它是一个漏洞。因为在片段视图被销毁后AdView仍然存在
  • 活动没有
    onDestroyView
    方法,视图在活动的
    onDestroy
    中被销毁。因此,我们在
    onDestroy
    中清除了对象
2.以编程方式从视图中删除AdView。
但是如果我们在XML中使用adView,那么这是不可能做到的

因为您需要从XML中膨胀AdView,所以删除
onDestroy
中的视图将为您完成这项工作

3.在
onDestroyView
ondestory
中设置AdView
null
。因此,AdView对象将不再被引用。垃圾收集器将对其进行清理


我希望这些信息对您有用。:)

在我的例子中,这是由于在lambda范围内使用MobileAds初始化代码和
this
引起的。将
this
更改为
applicationContext
后,它被修复

之前:

MobileAds.initialize(this, "ca-app-pub-0000000000000000~0000000000")
之后:

MobileAds.initialize(applicationContext, "ca-app-pub-0000000000000000~0000000000")

我尝试了stackoverflow论坛和所有其他Google论坛讨论的所有解决方案。内存泄漏似乎不稳定:如果用户在显示广告之前离开活动,则内存泄漏的可能性更高

唯一对我有效的方法就是在一个活动中包含内存泄漏

第一项建议: 不要将adview直接添加到XML布局文件中。如果您按照官方文档()中的说明操作,肯定会导致内存泄漏。 相反,按编程方式添加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>
    
  • 在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.
    
       if (mAdView != null){
            mAdView.setAdListener(null);
            adscontainer.removeAllViews();
            adscontainer = null;
            mAdView.destroy(); 
            mAdView = null;               
           }
    
    注意:这是为了在底部显示横幅,并带有“中心水平”和“对齐父项底部”选项

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

         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.
    
       if (mAdView != null){
            mAdView.setAdListener(null);
            adscontainer.removeAllViews();
            adscontainer = null;
            mAdView.destroy(); 
            mAdView = null;               
           }
    
  • 现在,让我们谈谈这个“MainActivity.MemoryLeakContainerActivity”: 是的,您必须牺牲一项活动来控制内存泄漏。我没有找到别的办法。我在这里尝试了“getApplicationContext()”,但没有成功。因此,我选择我的主要活动有两个原因:

    a-在我的应用程序中,MainActivity是一个简单的菜单,有三个按钮(“播放”、“查看套牌”和“创建套牌”)。它不会消耗太多内存,没有必要破坏它,因为我的应用程序中的大多数onBackPressed()任务会将流程引导到菜单,并且它将永远留在内存中

    b-这是第一个要加载的活动。因此,adview将随时准备好并可用于其他活动

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

     public static MainActivity MemoryLeakContainerActivity;
     public MainActivity() {
         super();
         if (MemoryLeakContainerActivity != null) {
         throw new IllegalStateException("MemoryLeakContainerActivity is already created");
         }
         MemoryLeakContainerActivity = this;
     }
    
  • 就这样!我的应用程序不再有内存泄漏!泄漏的金丝雀报告是干净的

  • 注意:这是一种变通方法,不是解决方案。解决方案应该只来自谷歌:一个简单的命令,比如“mAdView.destroy()”就可以了,对吗


    谷歌论坛正在讨论这个问题:

    不幸的是,仍然存在内存泄漏。看起来adView保留了对活动的引用。我从来没有在adView中遇到过任何内存泄漏,我们正在使用DFP。您能否确保您正在使用最新的播放服务。我肯定这是另外一回事,但如果没有看到一些源代码,我就不能说。你用LeakCanary或堆转储测试过应用程序吗?我使用的是最新的8.1.0播放服务,我使用的是LeakCanary。但这里有几个(潜在的)区别:1)我们使用DFP,即
    com.google.android.gms.ads.doubleclick.publisherView
    2)我不是在广告中使用片段,而是直接在活动视图树中添加和处理。我也有同样的问题,你解决了吗?但是如果我们使用XML格式的adView,那么这是做不到的。我试图销毁adView onDestroy,但没有任何帮助。@Funkyidol ya布局充气器很可能将该活动作为布局xml中adView的上下文提供。以编程方式向viewgroup添加视图并没有那么糟糕。这是我唯一能解决这个问题的方法。遗憾的是,我现在无法将其添加到XML布局中…我以编程方式添加AdView,并将其从片段的onStop()方法的父视图中删除,正如这里所提到的:我还使用应用程序上下文创建AdView的实例。这可能不适用于中介某些ad网络,因为某些中介适配器将