Admob Memory Leak - избегать с помощью пустой активности

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

В нашем приложении мы загружаем разные макеты, когда телефон вращается. Поэтому я вызываю следующий код при каждом повороте. При необходимости он создает 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)

Ваш ответ на вопрос