【问题标题】:Android AdMob causes memory leak?Android AdMob 导致内存泄漏?
【发布时间】:2011-09-03 04:15:49
【问题描述】:

我已将 AdMob v4.1.0 集成到我的应用程序中,它似乎导致了巨大的内存泄漏(很确定它已经发生在 4.0.4 上)。

为了隔离问题,我创建了一个带有空白线性布局的新项目并将 AdView 添加到其中(这实际上是 AdMob 提供的示例代码的复制和粘贴)。查看我的 main.xml、MainActivity.java 和清单内容:

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/linearLayout">
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
</LinearLayout>

MainActivity.java:

package AdsTry.main;

import com.google.ads.AdRequest;
import com.google.ads.AdSize;
import com.google.ads.AdView;

import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;

public class MainActivity extends Activity {

    private final int AD_VIEW_ID = 1000000; 

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

         // Lookup R.layout.main
        LinearLayout layout = (LinearLayout)findViewById(R.id.linearLayout);

        // Create the adView
        // Please replace MY_BANNER_UNIT_ID with your AdMob Publisher ID
        AdView adView = new AdView(this, AdSize.BANNER, "MY_BANNER_UNIT_ID");
        adView.setId(AD_VIEW_ID);

        // Add the adView to it
        layout.addView(adView);

        // Initiate a generic request to load it with an ad
        AdRequest request = new AdRequest();

        adView.loadAd(request);           
    }

    @Override
    protected void onPause() {
        Log.i("AdsTry", "onPause");

        getAdView().stopLoading();

        super.onPause();
    }

    @Override
    protected void onDestroy() {
        Log.i("AdsTry", "onDestroy");

        getAdView().destroy();

        super.onDestroy();
    }

    private AdView getAdView()
    {
        return (AdView) findViewById(AD_VIEW_ID);
    }
}

清单:

<application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".MainActivity"
              android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <!-- AdMobActivity definition -->
    <activity android:name="com.google.ads.AdActivity"
        android:configChanges="orientation|keyboard|keyboardHidden" />
</application>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

这就是我拥有的所有代码。

现在,当运行这个应用程序时,我可以看到 onPause 和 onDestory 都被调用并且 Activity 被终止,但问题是它永远不会被 GC 使用,因为它导致 InputMethodManager 持有对活动(查看活动被破坏后从 HPROF 输出中获取的图像):

一旦我删除了与 AdView 相关的代码(同样,这是此应用程序的唯一代码),问题就消失了:

编辑: 还尝试从 onCreate 中删除所有代码并更新 main.xml 以包含以下内容(仍然得到相同的结果):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/linearLayout">
    <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
    <com.google.ads.AdView
    android:id="@+id/Ad"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    ads:adUnitId="MY_ID"
    ads:adSize="BANNER"
    ads:loadAdOnCreate="true"/>
</LinearLayout>

有什么想法吗????

【问题讨论】:

  • 还没有尝试过,但我在 XDA 上得到了回复,说版本 4.1.1 应该可以解决这个问题。 forum.xda-developers.com/showthread.php?t=1077698
  • 我一直在我的项目中尝试使用 4.1.1,但我仍然遇到 Activity 泄漏。如果我删除 Admob,泄漏就会消失。如果我恢复到旧版本的 Admob,我仍然会遇到问题。如果我在 onDestroy 方法中调用 adView.onDestroy() 它没有帮助。如果我等待半小时并按 GC 数百次,它没有帮助。您是否找到任何解决方案或解决方法?我完全被困住了:(
  • 没有适合我的解决方案。最终删除了广告。只是不值得......
  • 我不确定这是否有帮助,但我在 Android 支持网站上创建了一个错误。 Admob 目前由 Google 持有,因此他们应该对此有所作为。我很惊讶这个错误已经不存在了。 ISSUE 59627

标签: android memory memory-leaks admob


【解决方案1】:

我正在使用“play-services-ads:7.5.0”,并且不需要创建 de AdMobActivity。它的工作原理是:

动态创建 adView

mAdView = new AdView(getApplicationContext(), AdSize.BANNER, banner_ad_unit_id); mAdsContainer.addView(mAdView);

在销毁和销毁 adView 时从 linearLayout 中删除所有视图

mAdView.setAdListener(null);
mAdsContainer.removeAllViews();
mAdView.destroy();

不幸的是,插页式广告仍然泄漏

【讨论】:

    【解决方案2】:

    这是我解决这个问题的方法:

    我通过使用相同的空活动实例限制了内存泄漏:

    public final class AdMobActivity
            extends Activity {
    
        public static AdMobActivity AdMobMemoryLeakWorkAroundActivity;
    
        public AdMobActivity() {
            super();
            if (AdMobMemoryLeakWorkAroundActivity != null)
                throw new IllegalStateException("This activity should be created only once during the entire application life");
            AdMobMemoryLeakWorkAroundActivity = this;
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            finish();
        }
    
        public static final void startAdMobActivity(Activity activity) {
            Intent i = new Intent();
            i.setComponent(new ComponentName(activity.getApplicationContext(), AdMobActivity.class));
            activity.startActivity(i);
        }
    }
    

    将使用 AdMobActivity.AdMobMemoryLeakWorkAroundActivity 创建更多广告。

    您当然还需要将活动添加到清单中:

    <activity
        android:launchMode="singleInstance"
        android:name="com.nu.art.software.android.modules.admob.AdMobActivity" />
    

    这个实现违背了我对静态引用的看法,静态引用不是常量,但是这个实现可以防止泄漏,因为只有一个活动实例用于创建所有广告,因此没有更多活动泄漏,加上活动完全是空的。

    注意:您应该从应用程序主活动 onCreate 方法调用 startAdMobActivity 方法。

    亚当。

    更新

    此解决方案仅在您动态创建广告并使用代码将其添加到布局中时才有效...并且不要忘记在 Activity.onDestroy() 中将其销毁。

    【讨论】:

    • 您的帖子看起来很有帮助,但我在开始使用它时遇到了麻烦。我的项目目前在每个活动布局 XML 中声明 com.google.ads.AdView,因此我不会在代码中动态生成任何广告或广告视图。我看不出您的活动如何与 admob 代码相关联。您能否发布一些有关如何实例化活动和加载广告的示例?谢谢!
    • 如果有人像我一样在遵循 TacB0sS 指令时遇到困难,请参阅stackoverflow.com/q/9558708/684893。我已经为我们这些没有经验的人分解了它。
    • 我试过这个解决方案。它不再泄露我的活动,但现在,当我点击任何广告时,它不会打开该链接。但是,如果我将当前活动实例传递给 AdView 构造函数,它可以正常工作。
    • 我很惊讶,经过这么长时间......(一年半)他们没有解决这个该死的问题......是的,你需要实例化 AdActivity 这样会有没有更多的泄漏,但为了展示您的广告,您需要传递当前的活动!
    • 我不明白为什么不,只要你动态添加adview,看@ravishi的回答
    【解决方案3】:

    解决此问题的一种方法是使用应用程序的基本活动创建一个单独的 adview 实例。将此广告视图添加到您想要的任何活动中,但请记住在销毁方法的活动中将其删除。

    这样,adview 只会泄漏基本活动,您几乎不应该拥有多个实例。

    例子:

    @Override
    public void onCreate(final Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       this.setContentView(R.layout.venue);
       mainLayout = (LinearLayout) findViewById(R.id.venueLayout);
       adview = StaticStateClass.getAdview();
    
       AdRequest request = new AdRequest();
       request.addKeyword(name);
       mainLayout.addView(adview);
       adview.loadAd(request);
    }
    
    @Override
    public void onDestroy() {
       mainLayout.removeView(adview);
       super.onDestroy();
    }
    

    【讨论】:

    • 如果您有时间,您可以使用反射手动破解 adview 对象并将您的上下文的引用设置为空。除非我别无选择,否则我通常不会这样做。
    【解决方案4】:

    我发现调用:-

    mAdView.destroy(); 
    

    在离开活动之前为我解决了泄漏。

    这是我在 xml 中声明横幅广告的方式:-

      <com.google.android.gms.ads.AdView
                android:id="@+id/adView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal|bottom"
                ads:adSize="BANNER"
                ads:adUnitId="@string/banner_ad_unit_id_2" />
    

    在 onCreate() 中获取对 admob 横幅的引用:-

    mAdView = (AdView) findViewById(R.id.adView2);
    AdRequest banneradRequest = new AdRequest.Builder().build();
    
    mAdView.loadAd(banneradRequest);
    

    并在活动的 ondestroy() 中销毁它:-

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mAdView.destroy();
    }
    

    【讨论】:

    • super.onDestroy();应该在 adView.destroy() 之后调用吧?
    • 是的 super.onDestroy() 应该在之后调用
    【解决方案5】:

    我也面临同样的问题。唯一对我有帮助的一件事 - 它的 System.exit(0)。我不喜欢它,但这是我找到的唯一方法。
    AdMob 只是停留在 RAM 4ever 中,不会让我的应用程序正常完成。当我重新启动我的应用程序时,操作系统只会引发那个不死应用程序,很快就会导致内存不足异常。
    My AdMob support forum topic - 目前不支持。看起来没有人在乎。

    【讨论】:

      【解决方案6】:

      解决此问题的另一个答案是遵循以下步骤,尽管这样做很痛苦。

      • 从 sharedPreferences 或任何其他类型的存储持久性中取消持久化所需的所有数据,
        将这些值存储在局部变量/对象中

      • 擦除所有应用数据

      • 重新坚持一切

      您可以在您的应用关闭时启动一项服务来执行此操作。这将防止数据由于内存泄漏而变得非常大。解决这个问题的方法很糟糕,但我已经尝试过了,它确实有效。

      【讨论】:

        【解决方案7】:

        以编程方式创建 AdView 并将 AdView 传递给任何 ViewGroup 像这样;

        MobileAds.initialize(applicationContext)
        adView = AdView(applicationContext)
        binding.adViewContainer.addView(adView)
        adView.adSize = getAdSize(binding.adViewContainer)
        adView.adUnitId = "ca-app-pub-3940256099942544/6300978111"
        val adRequest = AdRequest.Builder().build()
        adView.loadAd(adRequest)
        

        注意: 使用 applicationContext 代替 Activity 或 Fragment 上下文来防止内存泄漏

        我使用过视图绑定、测试广告、框架布局和自适应横幅。我在这里得到了获取最大宽度显示的算法:https://developers.google.com/admob/android/banner/adaptive?hl=en-US#sample_code

        override fun onDestroy() {
            adView.destroy()
            super.onDestroy()
            binding.adViewContainer.removeView(adView)
        }
        

        【讨论】:

          【解决方案8】:

          不要将您的 UI 与 Admob 合并,您可以运行另一个线程从 Admob 获取广告。您也可以在特定的时间间隔刷新广告。这会让您更容易调试内存问题的可能位置。

          谢谢 迪帕克

          【讨论】:

          • 感谢您的回答。将 AdMob 放在不同的线程上是什么意思?您的意思是 onCreate 上面的代码应该在不同的线程中运行吗?这将如何工作(它不应该在 UI 线程上运行吗?)
          • 我想说创建一个单独的线程,在 run() 方法中编写代码以从 Admob 获取广告。从这个主类开始该线程。让你的 UI 远离 admob 线程。
          • 我无法从 UI 线程外部访问 AdView 视图(出现异常)。我还尝试从 onCreate 中删除所有代码并更新 main.xml(请参阅问题的编辑)。仍然得到相同的结果。
          【解决方案9】:

          基本上,这不是一个真正的问题。正在发生的事情是您的活动正在接收 onDestroy,进行清理,然后等待 GC 运行。当 GC 发生时,它会看到这个旧上下文并清理所有底层垃圾,但它似乎并没有清除该通道中的 Activity——这就是为什么你的“泄露”的 Activity 占用空间如此之小:它是基本上是一个外壳。在下一次 GC 过程中,它将被清理。

          您可以通过启动活动、退出(触发 onDestroy)、返回进程、触发 GC 和 hprof 转储,稍等片刻,然后触发另一个 GC 和 hprof 转储来亲自看到这一点。在第一次转储中——至少在我的测试中——我看到了与你类似的结果:一个额外的活动,内存占用非常小。在第二个转储中,我只看到了当前正在运行的活动。

          【讨论】:

          • 我试过多次执行GC,等了几分钟又跑了一遍,结果还是一样...
          猜你喜欢
          • 2020-10-24
          • 2014-09-18
          • 1970-01-01
          • 2014-08-19
          • 2017-11-24
          • 1970-01-01
          • 2016-10-16
          • 1970-01-01
          • 2015-07-06
          相关资源
          最近更新 更多