Admob Memory Leak - evitando o uso de atividades vazias
Nosso aplicativo está sendo atingido com bastante força por um vazamento de memória. Descobri que a causa principal é o AdMob AdView mantendo referências a atividades antigas. O problema está muito bem documentado na perguntaAndroid AdMob causa vazamento de memória? e os sublinks nos comentários / respostas. Percebi que o problema não é aparente no ICS, pois o GC eventualmente limpa os WebViews com referências a atividades. No entanto, o meu pão de gengibre para HTC EVO 3D nunca recolhe as atividades e, considerando o número de relatórios de fechamento forçado devido a erros de OOM, o problema é muito difundido para o nosso aplicativ
Gostaria de seguir a solução fornecida pelo TacB0sS,https: //stackoverflow.com/a/8364820/68489. Ele sugeriu criar uma atividade vazia e usar a mesma atividade para cada AdMob AdView. O vazamento seria contido, pois o AdView manterá ativo apenas essa atividade vazia. Ele forneceu o código para a atividade em si e como fazer referência a ela, mas estou sem saber como realmente integrá-la ao nosso aplicativo. O código dele nunca chama nada do SDK da AdMob até onde sei.
No momento, estamos usando o AdView nos layouts XML, para que não façamos nada dinamicamente com os anúncios no código, como chamada loadAd (). Todos os nossos layouts com anúncios dependem do anúncio estar no XML, pois são apresentados em relação a ele. Minhas duas perguntas são: como implementar o código TacB0sS e como posso manter meus relacionamentos de layout XML se precisarmos mudar para criar os layouts XML no código?
Update 3/6:
brigado, Adam (TacB0sS) por responder! Não tenho problemas em mudar para criar o anúncio no código, mas ainda estou tendo dificuldades para usar sua atividade fictícia ao criar anúncios. Meu código atualmente é:
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);
Coloquei esse código no método onCreate da minha atividade inicial. Recebo uma força próxima da linha em que crio o AdView, "AdView adView = new AdView (...)". Snippet do 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
Qual é a maneira correta de inicializar sua AdMobActivity e referenciá-la ao criar o AdView? Obrigado novamente
Update 2 3/6:
Eu descobri meus problemas ao criar a atividade. Eu tenho sua solução totalmente implementada e a melhor parte é que ela resolve realmente meu vazamento de memória. Depois de passar duas semanas nesse problema, estou tão feliz que ele foi resolvido. Aqui estão as etapas completas que usei:
Crie uma nova atividade chamada 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);
}
}
Adicione o seguinte ao seu AndroidManifest.xml
<activity android:name="org.udroid.wordgame.AdMobActivity"
android:launchMode="singleInstance" />
Você precisa inicializar o AdMobActivity fictício antes de tentar carregar qualquer anúncio. Esta atividade não conterá nada. Ele será exibido por uma fração de segundo e, em seguida, fechado, retornando à atividade na qual você o chamou. Você não pode criá-lo na mesma atividade em que deseja carregar os anúncios, pois ele deve ser totalmente inicializado antes do uso. Eu o inicializo no onCreate de uma atividade de tela de carregamento inicial antes que a atividade principal que contém um anúncio seja iniciada:
// 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);
}
Agora você está pronto para criar anúncios em código. Adicione o seguinte LinearLayout ao seu layout de atividade XML. Alinhe todas as outras visualizações necessárias em torno deste layout. O AdView que criamos no código será colocado nessa visualização.
<LinearLayout
android:id="@+id/adviewLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
Na atividade em que você deseja carregar um anúncio, crie uma variável global para o AdView:
AdView adView;
No nosso aplicativo, carregamos layouts diferentes quando o telefone gira. Portanto, eu chamo o seguinte código em cada rotação. Ele cria o adView, se necessário, e o adiciona ao 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
Por fim, certifique-se de chamar adView.destroy () no método atividades onDestroy ():
@Override
protected void onDestroy() {
adView.destroy();
super.onDestroy();
}
Qualquer pessoa que leia isso, lembre-se de que esta é a solução de Adam (TacB0sS), não a minha. Eu só queria fornecer os detalhes completos da implementação para facilitar a implementação de outras pessoas. Esse bug da AdMob é um grande problema para aplicativos que executam pré-favo de mel e a solução de Adam é a melhor coisa que eu poderia encontrar para contorná-lo.E funciona!