【发布时间】:2012-03-22 10:56:59
【问题描述】:
我们的应用受到内存泄漏的严重影响。我发现根本原因是 AdMob AdView 保留对旧活动的引用。这个问题在问题Android AdMob causes memory leak? 和 cmets/answers 中的子链接中有很好的记录。我注意到这个问题在 ICS 中并不明显,因为 GC 最终会使用对活动的引用来清理 WebView。但是,我的 HTC EVO 3D running stockgingerbread 从不收集活动,考虑到由于 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 sn-p:
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
初始化 AdMobActivity 并在创建 AdView 时引用它的正确方法是什么?再次感谢!
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 错误对于运行 pre-honeycomb 的应用程序来说是一个巨大的问题,而 Adam 的解决方案是我能找到的最好的解决方法。 而且有效!
【问题讨论】:
-
像魅力一样工作!谢谢@ravishi 和@TacB0sS!
-
嗨 ravishi... 我在使用这个解决方案时遇到了问题。你能帮我么。这是我的问题stackoverflow.com/questions/15583994/…
-
是否有完整代码@ravishi 的链接?
-
解决这个问题的另一种方法,即使这样做很痛苦,也就是从 sharedPreferences 或任何其他类型的存储持久性中取消持久化所需的所有数据,擦除所有您的应用程序数据,然后重新坚持一切。您可以在您的应用程序关闭时启动一项服务来执行此操作。这将防止数据由于内存泄漏而变得非常大。
-
2015:我不得不求助于这个来解决我的应用程序由于 admob 导致的内存问题。我的应用程序现在就像一个魅力。非常感谢
标签: android memory-leaks admob