Admob Memory Leak - 通过使用空活动来避免

时间:2012-03-04 20:53:12

标签: android memory-leaks admob

我们的应用程序因内存泄漏而受到严重打击。我发现根本原因是AdMob AdView保留了对旧活动的引用。问题Android AdMob causes memory leak?以及评论/答案中的子链接都记录了这个问题。我注意到这个问题在ICS中并不明显,因为GC最终通过引用活动来清理WebViews。但是,我的HTC EVO 3D运行库存姜饼从不收集活动,并且由于OOM错误而考虑了关闭报告的数量,这个问题在我们的应用程序中非常普遍。

我想按照TacB0sS提供的解决方案, https://stackoverflow.com/a/8364820/684893。他建议创建一个空活动,并为每个AdMob AdView使用相同的活动。泄漏将被包含,因为AdView将只保留一个空活动。他提供了活动本身的代码以及如何引用它,但我不知道如何将它实际集成到我们的应用程序中。据我所知,他的代码从来没有从AdMob SDK调用任何东西。

我们目前在XML布局中使用AdView,因此我们不会动态地对代码中的广告执行任何操作,例如调用loadAd()。我们所有的广告布局都依赖于XML中的广告,因为它们是相对于它进行布局的。因此,我的两个问题是,如何实现TacB0sS代码以及如何在代码中切换到创建XML布局时如何保留XML布局关系?

更新3/6:

感谢Adam(TacB0sS)的回应!我在切换到代码中创建广告时没有问题,但在创建广告时我仍然难以使用您的虚拟活动。我的代码目前是:

AdMobActivity adActivity = new AdMobActivity();
adActivity.startAdMobActivity(this);

// Create an ad with the activity reference pointing to dummy activity
AdView adView = new AdView(adActivity.AdMobMemoryLeakWorkAroundActivity, AdSize.IAB_BANNER, "myAdUnitID");

// Create an ad request.
AdRequest adRequest = new AdRequest();

// add the ad to the layout and request it to be filled
RelativeLayout root_main = (RelativeLayout) findViewById(R.id.root_main);
root_main.addView(adView);
adView.loadAd(adRequest);

我已将此代码放在我初始活动的onCreate方法中。我在创建AdView的行上关闭了一个力量,“AdView adView = new AdView(...)”。 Stacktrace片段:

03-06 00:34:28.098 E/AndroidRuntime(16602): java.lang.RuntimeException: Unable to start activity ComponentInfo{org.udroid.wordgame/org.udroid.wordgame.MainMenu}: java.lang.NullPointerException
03-06 00:34:28.098 E/AndroidRuntime(16602):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1830)
(...)
03-06 00:34:28.098 E/AndroidRuntime(16602): Caused by: java.lang.NullPointerException
03-06 00:34:28.098 E/AndroidRuntime(16602):     at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:100)
03-06 00:34:28.098 E/AndroidRuntime(16602):     at com.google.ads.AdView.<init>(SourceFile:78)
03-06 00:34:28.098 E/AndroidRuntime(16602):     at org.udroid.wordgame.MainMenu.onCreate**(MainMenu.java:71)**  <- Line that creates the new AdView

在创建AdView时,初始化AdMobActivity并引用它的正确方法是什么?再次感谢!

更新2 3/6:

我想出了创建活动的问题。我已经完全实现了您的解决方案,最好的部分是实际上解决了我的内存泄漏。在这个问题上花了两个星期后,我很高兴它已经解决了。以下是我使用的完整步骤:

创建一项名为AdMobActivity的新活动:

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);
        Log.i("CHAT", "in onCreate - AdMobActivity");
        finish();
    }

    public static final void startAdMobActivity(Activity activity) {
        Log.i("CHAT", "in startAdMobActivity");
        Intent i = new Intent();
        i.setComponent(new ComponentName(activity.getApplicationContext(), AdMobActivity.class));
        activity.startActivity(i);
    }
}

将以下内容添加到AndroidManifest.xml

<activity android:name="org.udroid.wordgame.AdMobActivity"
    android:launchMode="singleInstance" />

您需要在尝试加载任何广告之前初始化虚拟AdMobActivity。此活动不包含任何内容。它会在一瞬间显示然后关闭,然后返回到您调用它的活动。您无法在要加载广告的同一活动中创建它,因为它必须在使用前及时完全初始化。我在包含广告的主要活动开始之前,在启动加载屏幕活动的onCreate中初始化它:

// Start the dummy admob activity.  Don't try to start it twice or an exception will be thrown
if (AdMobActivity.AdMobMemoryLeakWorkAroundActivity == null) {
    Log.i("CHAT", "starting the AdMobActivity");
    AdMobActivity.startAdMobActivity(this);
}

现在您已准备好在代码中制作广告。将以下LinearLayout添加到XML活动布局中。对齐此布局所需的所有其他视图。我们在代码中创建的AdView将放置在此视图中。

<LinearLayout
android:id="@+id/adviewLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />

在您要加载广告的活动中,为AdView创建一个全局变量:

AdView adView;

在我们的应用中,我们在手机旋转时加载不同的布局。因此,我在每次旋转时调用以下代码。如有必要,它会创建adView并将其添加到adviewLayout。

    // DYNAMICALLY CREATE AD START
    LinearLayout adviewLayout = (LinearLayout) findViewById(R.id.adviewLayout);
    // Create an ad.
    if (adView == null) {
        adView = new AdView(AdMobActivity.AdMobMemoryLeakWorkAroundActivity, AdSize.BANNER, "<ADUNITID>");
        // Create an ad request.
        AdRequest adRequest = new AdRequest();
        // Start loading the ad in the background.
        adView.loadAd(adRequest);
        // Add the AdView to the view hierarchy. The view will have no size until the ad is loaded.
        adviewLayout.addView(adView);
    }
    else {
        ((LinearLayout) adView.getParent()).removeAllViews();
        adviewLayout.addView(adView);
        // Reload Ad if necessary.  Loaded ads are lost when the activity is paused.
        if (!adView.isReady() || !adView.isRefreshing()) {
            AdRequest adRequest = new AdRequest();
            // Start loading the ad in the background.
            adView.loadAd(adRequest);
        }
    }
    // DYNAMICALLY CREATE AD END

最后,请确保在活动onDestroy()方法中调用adView.destroy():

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

其他任何读过此内容的人,请记住这是亚当的解决方案(TacB0sS),而不是我的解决方案。我只是想提供完整的实现细节,以便其他人更容易实现。这个AdMob错误对于运行预蜂窝的应用程序来说是一个巨大的问题,而Adam的解决方案是我能找到的绕过它的最好的方法。 它有效!

3 个答案:

答案 0 :(得分:17)

Ravishi,

你的问题是关键,我没有在我的解决方案中解决它。据我所知,解决方案我发现只能动态地工作,你可以在调用sdk时选择你的活动......

我的代码没有使用示例的原因是因为我的解决方案比我提出的解决方案稍微复杂一点,涉及我围绕Android框架构建的整个包装框架,其中AdMob与应用程序的关系是通过一个中间模块,使用单个活动实例动态放置广告。

我真的怀疑你可以简单地使用Android XML来避免内存泄漏。

在任何情况下,如果你进入内存泄漏业务,你也可以查看你的AsyncTask使用情况......它也有自己的内存泄漏行为......所以这是我的solution

祝你好运......

- 更新 - 2014年10月7日

有人刚刚回答了我的答案,这个问题仍然存在,这已经过去了近三年,我的原始答案已经差不多三年了,因为来自谷歌的AdMob ......人们的应用程序仍有内存泄漏...的Android ....

无论如何,我只想补充一点,你应该将AdMobActivity的主题设置为透明,它可以防止闪烁。

- 更新 - 2016年2月28日

四年......

- 更新 - 09/03/17

五年...... 谷歌的某个人请醒来,并聘请真正的猴子来完成这项工作:)

亚当。

答案 1 :(得分:6)

我正在使用&#34;播放服务广告:7.5.0&#34;并且创建de AdMobActivity并不是必需的。 它的工作原理是:

  • 以动态方式创建广告

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

  • 在销毁和销毁adView时删除linearLayout的所有视图

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

不幸的非页内广告仍在泄密

答案 2 :(得分:1)

我在6.1.0 SDK中看到了同样的泄漏,但是能够通过在我的Activity的onDestroy中调用相关AdView上的destroy()来解决它。我认为他们已经修好了。 destroy()似乎摆脱了AdView的WebView对我的Activity的PhantomReferences,这使得Activity不能成为GC。