AdMob Memory Leak-unikanie przez użycie pustej aktywności

[9]}nasza aplikacja jest bardzo mocno uderzona przez wyciek pamięci. Odkryłem, że główną przyczyną jest AdMob adview zachowujący odniesienia do starych działań. Problem jest dość dobrze udokumentowany w pytaniu Android AdMob powoduje wyciek pamięci? i podpowiedzi w komentarzach/odpowiedziach. Zauważyłem, że problem nie jest widoczny w ICS, ponieważ GC ostatecznie oczyszcza WebViews z odniesieniami do działań. Jednak mój HTC EVO 3D running stock gingerbread nigdy nie zbiera działania i biorąc pod uwagę liczbę raportów force close z powodu błędów OOM, problem jest bardzo powszechny dla naszej aplikacji.

Chciałbym podążać za rozwiązaniem dostarczonym przez TacB0sS, https://stackoverflow.com/a/8364820/684893. zasugerował utworzenie pustej aktywności i użycie tej samej aktywności dla każdego AdMob AdView. Przeciek zostałby powstrzymany, ponieważ AdView utrzyma tylko jedną pustą aktywność. Podał kod do samej czynności i jak odwołaj się do niego, ale nie wiem, jak właściwie zintegrować go z naszą aplikacją. Jego kod nigdy nie wywołuje niczego z AdMob SDK, o ile wiem.

Obecnie używamy AdView w układach XML, więc nie robimy dynamicznie niczego z reklamami w kodzie, takimi jak wywołanie loadAd (). Wszystkie nasze layouty z reklamami polegają na tym, że reklama jest w XML, ponieważ są ułożone względem niej. Moje dwa pytania są więc, jak zaimplementować Kod TacB0sS i jak mogę zachować moje relacje układu XML czy musimy przełączyć się na tworzenie układów XML w kodzie?

Aktualizacja 3/6:

Dzięki Adam (TacB0sS) za odpowiedź! Nie mam problemu z przełączeniem się na tworzenie reklamy w kodzie, ale nadal mam problemy z używaniem fałszywej aktywności podczas tworzenia reklam. Obecnie Mój kod to:

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);

Umieściłem ten kod w metodzie onCreate mojej początkowej aktywności. I get a force close on the line where I create the AdView, " AdView adView = new AdView (...)". Stacktrace fragment:

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

Jaki jest właściwy sposób zainicjowania swojej AdMobActivity i odwoływania się do niej podczas tworzenia AdView? Jeszcze raz dziękuję!

Aktualizacja 2 3/6:

Rozgryzłem swoje problemy tworząc aktywność. Mam Twoje rozwiązanie w pełni zaimplementowane, a najlepsze jest to, że to rozwiązuje mój wyciek pamięci . Po spędzeniu dwóch tygodni na tym problemie, jestem tak szczęśliwy, że został rozwiązany. Oto pełne kroki, których użyłem:

Utwórz nową aktywność o nazwie 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);
    }
}

Dodaj następujące do AndroidManifest.xml

<activity android:name="org.udroid.wordgame.AdMobActivity"
    android:launchMode="singleInstance" />

Musisz zainicjować atrapę AdMobActivity przed próbą załadowania jakichkolwiek reklam. Ta aktywność nic nie zawiera. Będzie on wyświetlany przez ułamek sekundy, a następnie zamykany, powracając do aktywności, którą wywołałeś. Nie można utworzyć go w tej samej aktywności, którą chcesz załadować reklamy, ponieważ musi być w pełni zainicjowany na czas przed użyciem. I inicjalizacji go w ekranie ładowania splash aktywność jest onCreate przed rozpoczęciem głównej aktywności, która Zawiera reklamę:

// 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);
}

Teraz jesteś gotowy do tworzenia reklam w kodzie. Dodaj następujący LinearLayout do układu aktywności XML. Wyrównaj wszystkie inne niezbędne widoki wokół tego układu. Widok, który tworzymy w kodzie, zostanie umieszczony wewnątrz tego widoku.

<LinearLayout
android:id="@+id/adviewLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />

W aktywności, którą chcesz załadować reklamę, Utwórz zmienną globalną dla AdView:

AdView adView;

W naszej aplikacji ładujemy różne układy, gdy telefon się obraca. / Align = "left" / W razie potrzeby wytworzy program adView i doda go do programu 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
Na koniec zadzwoń do adView.destroy () w metodzie activities onDestroy ():
@Override
protected void onDestroy() {
    adView.destroy();
super.onDestroy();
}
Jeśli ktoś jeszcze to przeczyta, proszę pamiętać, że to rozwiązanie Adama (TacB0sS), a nie moje. Chciałem tylko podać pełne szczegóły wdrożenia, aby ułatwić innym wdrożenie. Ten błąd AdMob jest ogromnym problemem dla uruchomionych aplikacji pre-honeycomb i rozwiązanie Adama jest najlepszą rzeczą, jaką mogłem znaleźć, aby go obejść. I to działa!
Author: Community, 2012-03-05

3 answers

Ravishi,

Twoje pytanie jest do rzeczy, i nie udało mi się rozwiązać go w moim rozwiązaniu. O ile mogę powiedzieć, że znalezione rozwiązanie działa tylko dynamicznie, gdzie można wybrać swoją aktywność podczas wywoływania sdk...

Powodem, dla którego mój kod nie ma przykładu użycia, jest to, że moje rozwiązanie jest nieco bardziej skomplikowane niż to, które przedstawiłem, obejmujące cały Framework owijania, który zbudowałem wokół Android framework, gdzie relacja AdMob do aplikacji jest poprzez moduł pośredni, który dynamicznie umieszcza reklamę za pomocą pojedynczej instancji aktywności.

Naprawdę wątpię, że można uniknąć wycieku pamięci po prostu za pomocą XML Android.

W każdym razie, jeśli zajmujesz się wyciekiem pamięci, równie dobrze możesz sprawdzić użycie Asynctasku... ma również własne zachowanie wycieku pamięci... oto moje rozwiązanie

Powodzenia...

-- UPDATE -- 07/10/14

Ktoś właśnie podniósł moją odpowiedź, jej propustorase że ten problem nadal istnieje, minęło prawie trzy lata od mojej pierwotnej odpowiedzi, a ludzie nadal mają wycieki pamięci w swoich aplikacjach z powodu AdMob... od Google... To stworzyło Androida....

W każdym razie chciałem tylko dodać, że powinieneś chyba ustawić temat AdMobActivity na przezroczysty, zapobiegnie to migotaniu.

-- UPDATE -- 28/02/16

Cztery lata...

-- UPDATE -- 09/03/17

Pięć lat... Niech ktoś z Google się obudzi i wynajmij prawdziwą małpę do roboty:)

Adam.

 16
Author: TacB0sS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-09 21:11:13

Używam "play-services-ads: 7.5.0" i nie trzeba było tworzyć de AdMobActivity. Zadziałało przez:

  • Tworzenie adView

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

  • Usuwanie wszystkich widoków z linearLayout na destroy i Destroy adView

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

Niestety śródmiąższowe wciąż przecieki

 6
Author: Julian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-18 18:33:00

Widziałem ten sam wyciek z SDK 6.1.0, ale udało mi się go rozwiązać, wywołując destroy() na AdView, o którym mowa w mojej aktywności onDestroy. Chyba to naprawili. The destroy ()

 1
Author: Steven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-08 21:05:48