Admob内存泄漏-通过使用空活动来避免



我们的应用程序受到内存泄漏的严重打击。我发现根本原因是AdMob AdView保留了对旧活动的引用。这个问题在问题中有很好的记录,安卓AdMob导致内存泄漏?以及评论/答案中的子链接。我注意到这个问题在ICS中并不明显,因为GC最终会通过引用活动来清理WebView。然而,我的HTC EVO 3D运行股票姜饼从未收集活动,考虑到OOM错误导致的强制关闭报告的数量,这个问题在我们的应用程序中非常普遍。

我想遵循TacB0sS提供的解决方案,https://stackoverflow.com/a/8364820/684893.他建议创建一个空的活动,并为每个AdMob AdView使用相同的活动。泄漏将得到控制,因为AdView只会保持一个空活动的活力。他提供了活动本身的代码以及如何引用它,但我不知道如何将它真正集成到我们的应用程序中。据我所知,他的代码从未调用过AdMob SDK中的任何内容。

我们目前在XML布局中使用AdView,所以我们不会在调用loadAd()等代码中动态地处理广告。我们所有的广告布局都依赖于XML中的广告,因为它们是相对于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=新的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

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

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

其他读过这篇文章的人,请记住这是亚当的解决方案(TacB0sS),而不是我的。我只是想提供完整的实现细节,让其他人更容易实现。这个AdMob错误对于运行蜂窝之前的应用程序来说是一个巨大的问题,Adam的解决方案是我能找到的最好的解决方案。而且它很有效

Ravishi,

你的问题切中要害,我在解决方案中没有解决这个问题。据我所知,我发现的解决方案只能动态工作,您可以在调用sdk时选择活动。。。

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

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

在任何情况下,如果您从事内存泄漏业务,您不妨检查一下AsyncTask的使用情况。。。它也有自己的内存泄漏行为。。。这是我的解决方案

祝你好运。。。

--更新--07/10/14

有人刚刚投票支持我的答案,这个问题仍然存在,这太荒谬了,距离我最初的答案已经快三年了,人们的应用程序中仍然因为AdMob而内存泄漏。。。来自谷歌。。。这使得Android。。。。

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

--更新--2016年2月28日

四年。。。

--更新--09/03/17

五年。。。谷歌的某个人请醒来,雇佣真正的猴子来做这项工作:)

亚当。

我使用的是"播放服务广告:7.5.0",没有必要创建de AdMobActivity。工作人员:

  • 创建adView普通

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

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

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

不幸的是,Interstial仍然泄漏

我在6.1.0 SDK中看到了同样的泄漏,但通过在我的活动的onDestroy中对有问题的AdView调用destroy()来解决它。我想他们已经解决了这个问题。destroy()似乎消除了AdView的WebView对我的活动的PhantomReferences,这使活动无法成为GC。

最新更新