Наше приложение довольно сильно пострадало от утечки памяти.Я обнаружил, что основной причиной является AdMob AdView, хранящий ссылки на старые действия.Проблема довольно хорошо задокументирована в вопросе Android AdMob вызывает утечку памяти? и подлинки в комментариях / ответах.Я заметил, что проблема не очевидна в ICS, поскольку GC в конечном итоге очищает WebViews со ссылками на действия.Однако мой пряник HTC EVO 3D никогда не собирает действия, и, учитывая количество отчетов о принудительном закрытии из-за ошибок OOM, проблема очень распространена для нашего приложения.
Я хотел бы следовать решению, предоставленномуTacB0sS, https://stackoverflow.com/a/8364820/684893. Он предложил создать пустое действие и использовать это же действие для каждого AdMob AdView.Утечка будет сдерживаться, поскольку AdView будет поддерживать только одно пустое действие.Он предоставил код для самого действия и как на него ссылаться, но я не знаю, как на самом деле интегрировать его в наше приложение.Насколько я могу судить, его код никогда ничего не вызывает из AdMob SDK.
В настоящее время мы используем AdView в макетах XML, поэтому динамически ничего не делаем с объявлениями в коде, такими как call loadAd ().Все наши макеты с рекламой основаны на том, что реклама находится в XML, поскольку они размещены относительно нее.Таким образом, у меня два вопроса: как мне реализовать код TacB0sS и как я могу сохранить свои отношения макета XML, если нам нужно переключиться на создание макетов XML в коде?
Обновление 3/6:
Спасибо Адаму (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 (...)".Фрагмент стека:
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;
Inнаше приложение, мы загружаем разные макеты, когда телефон вращается.Поэтому я вызываю следующий код при каждом повороте.При необходимости он создает 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
Наконец, убедитесь, что вы вызываете adView.destroy () в методе onDestroy () операций:
@Override
protected void onDestroy() {
adView.destroy();
super.onDestroy();
}
Всем, кто читает это, пожалуйста, помните, что это решение Адама (TacB0sS), а не мое. Я просто хотел предоставить полную информацию о реализации, чтобы другим было проще ее реализовать. Эта ошибка AdMob - огромная проблема для приложений, использующих предварительные соты, и решение Адама - лучшее, что я могу найти, чтобы обойти его. И это работает!