Подтвердить что ты не робот

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>

Любые идеи????

4b9b3361

Ответ 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. На первом дампе - по крайней мере, на моих тестах - я видел результаты, похожие на ваши: дополнительная активность лежала с очень небольшим объемом памяти. Во втором дампе я видел только текущую активность.