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