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 после уничтожения активности):
![MainActivity Merge shortest path to GC Roots]()
Как только я удалю код, связанный с AdView (и опять же, это ТОЛЬКО код этого приложения) проблема исчезнет:
![Same HPROF output without using AdView]()
EDIT:
Также попытался удалить ВСЕ код из 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>
Любые идеи????
Ответы
Ответ 1
Я использую "play-services-ads: 7.5.0", и не нужно было создавать AdMobActivity. Он работал:
Создание adView динамически
mAdView = new AdView(getApplicationContext(), AdSize.BANNER, banner_ad_unit_id); mAdsContainer.addView(mAdView);
Удаление всех представлений из linearLayout для уничтожения и уничтожения adView
mAdView.setAdListener(null);
mAdsContainer.removeAllViews();
mAdView.destroy();
К сожалению, Interstitial все еще течет
Ответ 2
Вот моя работа для этого беспорядка:
Я ограничил утечку памяти, используя тот же пустой экземпляр активности:
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.
Адам.
UPDATE
Это решение работает только при динамическом создании объявления и добавлении его в макет с кодом... и не забудьте его уничтожить в Activity.onDestroy().
Ответ 3
Один из способов устранить эту проблему - создать один экземпляр рекламного объявления, используя базовую активность вашего приложения. Добавьте это объявление в любое действие, которое вы хотите, но не забудьте удалить его в методах уничтожения.
Таким образом, реклама только теряет базовую активность, к которой у вас почти никогда не должно быть более одного экземпляра.
Пример:
@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();
}
Ответ 4
Я узнал, что вызов: -
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();
}
Ответ 5
У меня такая же проблема. Единственное, что мне помогло - это System.exit(0). Мне это не нравится, но это единственный способ, который я нашел.
AdMob просто остается в RAM 4ever и не позволяет моему приложению закончить правильно. Когда я перезагружаю приложение, ОС просто поднимает это приложение undead, и вскоре он вызывает исключение из памяти.
Мой форум поддержки форума AdMob - пока нет поддержки. Похоже, no1 заботится.
Ответ 6
Другой ответ, который решает эту проблему, даже если это больно, заключается в следующем:
-
отменить все данные, которые необходимы для sharedPreferences или любых других типов хранения на жестком диске,
хранить эти значения в локальных переменных/объектах
-
ПРОСМОТР ВСЕХ ВАШИХ ДАННЫХ APP
-
повторно сохранить все
Вы можете запустить службу, когда ваше приложение закрывается, чтобы сделать это. Это предотвратит получение данных из-за утечки памяти. Неприятный способ справиться с этим, но я попробовал, и он работает.
Ответ 7
Утечка памяти в SDK AdMob действительно серьезная проблема, новая версия firebase-ads:11.6.0
теперь, но она все еще течет с памятью.
Это мое обходное решение, чтобы исправить это:
https://gist.github.com/Khang-NT/9fcb49fa2a0141a7bd3ab10c3451af1a
Заменить это
MobileAds.getRewardedVideoAdInstance(activityContext);
new AdView(activityContext);
// ...everything inject to AdMob SDK related to Activity context
С помощью этого:
MobileAds.getRewardedVideoAdInstance(new FixActivityLeakAdMob(activityContext));
new AdView(new FixActivityLeakAdMob(activityContext));
Ответ 8
Не объединяя свой интерфейс с Admob, вы можете запустить другой поток, чтобы получить объявление от Admob. Также вы можете обновить объявление в определенный промежуток времени. Это будет проще для вас отладки, где проблема с памятью может быть.
Спасибо
Дипак
Ответ 9
В принципе, это не проблема. То, что происходит в вашей деятельности, - это получение onDestroy, его очистка, а затем ожидание работы GC. Когда GC происходит, он видит, что этот старый контекст лежит вокруг и очищает весь основной гнус, но, похоже, он не очищает активность в этом проходе - вот почему ваша "просочившаяся" активность имеет такой небольшой след: это в основном оболочкой. На следующем проходе GC он будет очищен.
Вы можете увидеть это сами, активировав свою деятельность, отступив (чтобы запустить onDestroy), возвращаясь к процессу, запустив GC и дамп hprof, немного подождав, а затем запустив другой свалку GC и hprof. На первом дампе - по крайней мере, на моих тестах - я видел результаты, похожие на ваши: дополнительная активность лежала с очень небольшим объемом памяти. Во втором дампе я видел только текущую активность.