我们的应用程序受到内存泄漏的严重打击。我发现根本原因是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。