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

Утечка памяти Admob - избегая пустых действий

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

4b9b3361

Ответ 1

Ravishi,

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

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

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

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

Желаем удачи...

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

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

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

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

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

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

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

Адам.

Ответ 2

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

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

    mAdView = новый AdView (getApplicationContext(), AdSize.BANNER, banner_ad_unit_id); mAdsContainer.addView(mAdView);

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

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

К сожалению, Interstitial все еще течет

Ответ 3

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