Android AdMob вызывает утечку памяти?
Я интегрировал AdMob v4.1.0 в свое приложение, и это, кажется, вызвало огромную утечку памяти (почти уверен, что это уже произошло в 4.0.4).
Чтобы изолировать проблему, я создал новый проект с пустым линейным макетом и добавил в него AdView (это фактически копирование и вставка из примера кода, предоставленного AdMob). Смотрите мой main.xml, MainActivity.java и контент манифеста:
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/linearLayout">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
MainActivity.java:
package AdsTry.main;
import com.google.ads.AdRequest;
import com.google.ads.AdSize;
import com.google.ads.AdView;
import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
private final int AD_VIEW_ID = 1000000;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Lookup R.layout.main
LinearLayout layout = (LinearLayout)findViewById(R.id.linearLayout);
// Create the adView
// Please replace MY_BANNER_UNIT_ID with your AdMob Publisher ID
AdView adView = new AdView(this, AdSize.BANNER, "MY_BANNER_UNIT_ID");
adView.setId(AD_VIEW_ID);
// Add the adView to it
layout.addView(adView);
// Initiate a generic request to load it with an ad
AdRequest request = new AdRequest();
adView.loadAd(request);
}
@Override
protected void onPause() {
Log.i("AdsTry", "onPause");
getAdView().stopLoading();
super.onPause();
}
@Override
protected void onDestroy() {
Log.i("AdsTry", "onDestroy");
getAdView().destroy();
super.onDestroy();
}
private AdView getAdView()
{
return (AdView) findViewById(AD_VIEW_ID);
}
}
проявляются:
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- AdMobActivity definition -->
<activity android:name="com.google.ads.AdActivity"
android:configChanges="orientation|keyboard|keyboardHidden" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
И это весь код, который у меня есть.
Теперь, когда я запускаю это приложение, я вижу, что и onPause, и onDestory вызываются, и действие прекращается, НО проблема заключается в том, что оно никогда не будет доступно для GC, поскольку оно заставляет InputMethodManager содержать ссылку на действие (см. изображение взято с вывода HPROF после уничтожения действия):
Как только я удаляю код, связанный с AdView (и снова, это ЕДИНСТВЕННЫЙ код этого приложения), проблема исчезает:
РЕДАКТИРОВАТЬ: Также попытался удалить ВСЕ код из onCreate и обновил файл main.xml, чтобы он содержал следующее (все равно получайте тот же результат):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/linearLayout">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<com.google.ads.AdView
android:id="@+id/Ad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
ads:adUnitId="MY_ID"
ads:adSize="BANNER"
ads:loadAdOnCreate="true"/>
</LinearLayout>
Есть идеи????
9 ответов
Я использую "play-services-ads:7.5.0", и не было необходимости создавать de AdMobActivity. Работало:
Создание adView динамически
mAdView = new AdView(getApplicationContext(), AdSize.BANNER, banner_ad_unit_id); mAdsContainer.addView(mAdView);
Удаление всех представлений из linearLayout при уничтожении и уничтожении adView
mAdView.setAdListener(null);
mAdsContainer.removeAllViews();
mAdView.destroy();
К сожалению, Интерстициальный все еще утечки
Вот моя работа вокруг этого беспорядка:
Я ограничил утечку памяти, используя тот же пустой экземпляр действия:
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);
finish();
}
public static final void startAdMobActivity(Activity activity) {
Intent i = new Intent();
i.setComponent(new ComponentName(activity.getApplicationContext(), AdMobActivity.class));
activity.startActivity(i);
}
}
Дальнейшее объявление будет создано с использованием AdMobActivity.AdMobMemoryLeakWorkAroundActivity.
Вы также должны добавить действие к манифесту, конечно:
<activity
android:launchMode="singleInstance"
android:name="com.nu.art.software.android.modules.admob.AdMobActivity" />
Эта реализация противоречит моим убеждениям относительно статических ссылок, которые не являются константами, но эта реализация предотвращает утечку, потому что только один экземпляр действия используется для создания всех объявлений, и, следовательно, больше нет утечек активности, плюс тот факт, что действие полностью пусто,
ПРИМЕЧАНИЕ. Вы должны вызывать метод startAdMobActivity из основного метода действия приложения onCreate.
Адам.
ОБНОВИТЬ
Это решение работает, только если вы создаете объявление динамически и добавляете его в макет с кодом... и не забудьте уничтожить его в Activity.onDestroy().
Одним из способов решения этой проблемы является создание одного отдельного экземпляра рекламы, используя базовую активность вашего приложения. Добавьте это объявление к любому желаемому действию, но не забудьте удалить его в действиях метода уничтожения.
Таким образом, объявление только пропускает базовую активность, которая почти никогда не должна иметь более одного экземпляра.
Пример:
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.venue);
mainLayout = (LinearLayout) findViewById(R.id.venueLayout);
adview = StaticStateClass.getAdview();
AdRequest request = new AdRequest();
request.addKeyword(name);
mainLayout.addView(adview);
adview.loadAd(request);
}
@Override
public void onDestroy() {
mainLayout.removeView(adview);
super.onDestroy();
}
Я узнал, что зовет:-
mAdView.destroy();
перед тем как уйти с активности решаю утечку за меня.
Вот как я объявляю рекламные баннеры в формате xml:
<com.google.android.gms.ads.AdView
android:id="@+id/adView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
ads:adSize="BANNER"
ads:adUnitId="@string/banner_ad_unit_id_2" />
Получить ссылку для баннера admob в onCreate():-
mAdView = (AdView) findViewById(R.id.adView2);
AdRequest banneradRequest = new AdRequest.Builder().build();
mAdView.loadAd(banneradRequest);
и уничтожить его в ondestroy() действия:-
@Override
protected void onDestroy() {
super.onDestroy();
mAdView.destroy();
}
Я сталкиваюсь с той же проблемой. Единственное, что мне помогло - это System.exit(0). Мне это не нравится, но это единственный способ, который я нашел.
AdMob просто остается в оперативной памяти 4ever и не позволяет моему приложению завершиться правильно. Когда я перезагружаю свое приложение, ОС просто вызывает это неживое приложение, и вскоре оно вызывает исключение нехватки памяти.
Моя тема на форуме поддержки AdMob - пока поддержки нет. Похоже, № 1 заботится.
Другой ответ, который решает эту проблему, несмотря на то, что это трудно, - выполнить следующие шаги.
убрать все данные, которые необходимы из sharedPreferences или любых других типов, в отношении сохранности хранилища,
хранить эти значения в локальных переменных / объектахПротрите все данные вашего приложения
повторять все
Вы можете запустить службу, когда ваше приложение закрывается, чтобы сделать это. Это предотвратит увеличение объема данных из-за утечки памяти. Гадкий способ решения этой проблемы, но я попробовал, и это работает.
Программно создайте AdView и передайте AdView в любую ViewGroup. Пример:
MobileAds.initialize(applicationContext)
adView = AdView(applicationContext)
binding.adViewContainer.addView(adView)
adView.adSize = getAdSize(binding.adViewContainer)
adView.adUnitId = "ca-app-pub-3940256099942544/6300978111"
val adRequest = AdRequest.Builder().build()
adView.loadAd(adRequest)
Примечание. Используйте applicationContext вместо контекста активности или фрагмента, чтобы предотвратить утечку памяти.
Я использую привязку к просмотру, тестовые объявления, размещение кадров и адаптивный баннер. У меня есть алгоритм отображения максимальной ширины здесь: https://developers.google.com/admob/android/banner/adaptive?hl=en-US#sample_code
override fun onDestroy() {
adView.destroy()
super.onDestroy()
binding.adViewContainer.removeView(adView)
}
Не объединяйте свой пользовательский интерфейс с Admob. Вы можете запустить другой поток, чтобы получить рекламу от Admob. Также вы можете обновить объявление через определенный промежуток времени. Вам будет легче отлаживать там, где может быть проблема с памятью.
Спасибо Дипак
По сути, это не проблема. Что происходит, так это то, что ваша деятельность получает onDestroy, выполняет его очистку, а затем ждет запуска GC. Когда происходит сборщик мусора, он видит этот старый контекст, лежащий вокруг, и очищает весь основной ганк, но, похоже, он не очищает активность в этом проходе - вот почему ваша "утечка" активности имеет такой маленький след: в основном оболочка. На следующем проходе GC он будет очищен.
Вы можете убедиться в этом сами, активизировав свою деятельность, отступив (чтобы вызвать onDestroy), вернувшись в процесс, запустив GC и дамп hprof, немного подождав, затем запустив еще один GC и дамп hprof. В первом дампе - по крайней мере, в моем тестировании - я увидел результаты, похожие на ваши: дополнительная активность с очень маленьким объемом памяти. Во втором дампе я увидел только текущую активность.