Admob Memory Leak - избегать с помощью пустой активности - PullRequest
26 голосов
/ 05 марта 2012

Наше приложение довольно сильно пострадало от утечки памяти.Я обнаружил, что основной причиной является 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 - огромная проблема для приложений, использующих предварительные соты, и решение Адама - лучшее, что я могу найти, чтобы обойти его. И это работает!

Ответы [ 3 ]

17 голосов
/ 05 марта 2012

Ravishi,

Ваш вопрос к сути, и я не смог ответить на него в своем решении. Насколько я могу судить, решение, которое я нашел, работает только динамически, где вы можете выбрать свою активность при вызове sdk ...

Причина, по которой в моем коде нет примера использования, заключается в том, что мое решение немного сложнее, чем представленное мной, и включает в себя всю оболочку, которую я построил на платформе Android, где отношение AdMob к приложению через промежуточный модуль, который размещает объявление динамически с использованием одного экземпляра действия.

Я действительно сомневаюсь, что вы можете избежать утечки памяти, просто используя Android XML.

В любом случае, если вы занимаетесь утечкой памяти, вы также можете проверить использование AsyncTask ... у него также есть свое поведение утечки памяти ... так что вот мое решение

удачи ...

- ОБНОВЛЕНИЕ - 07/10/14

Кто-то только что проголосовал за мой ответ, нелепо, что эта проблема все еще существует, прошло почти три года с момента моего первоначального ответа, и у людей все еще есть утечки памяти в их приложениях из-за AdMob ... от Google ... который сделал Android ....

В любом случае, я просто хотел добавить, что вам, вероятно, следует установить прозрачную тему AdMobActivity, чтобы предотвратить мерцание.

- ОБНОВЛЕНИЕ - 28/02/16

Четыре года ...

- ОБНОВЛЕНИЕ - 09/03/17

Пять лет ... Кто-то в Google, пожалуйста, проснитесь и наймите настоящую обезьяну, чтобы сделать работу:)

Адам.

6 голосов
/ 18 июня 2015

Я использую "play-services-ads: 7.5.0", и создание Ad AdMobActivity не требовалось.Работали:

  • Создание adView динамически

    mAdView = new AdView (getApplicationContext (), AdSize.BANNER, banner_ad_unit_id);mAdsContainer.addView (mAdView);

  • Удаление всех представлений из linearLayout при уничтожении и уничтожении adView

                mAdView.setAdListener(null);
                mAdsContainer.removeAllViews();
                mAdView.destroy();
    

К сожалению, промежуточные утечки все еще

1 голос
/ 09 июля 2013

Я наблюдал эту же утечку в 6.1.0 SDK, но смог ее устранить, вызвав destroy () для соответствующего AdView в onDestroy моей Activity. Я думаю, что они это исправили. Функция destroy (), похоже, избавляет от PhantomReferences, которые были у WebView AdView для моей Activity, которые не давали Activity быть GC.

...