android - ¿Qué está causando que Google AdMob pierda ServiceConnection?



android-fragments (2)

Estaba teniendo un problema similar con los anuncios de AdMob (Google Play Services) que filtran grandes cantidades de memoria. Usé MAT para encontrar que el problema era que cada anuncio de gms estaba siendo retenido por mi instancia de aplicación, en la matriz sComponentCallbacks. Así que anulo registerComponentCallbacks() y unregisterComponentCallbacks() para hacer un seguimiento de las instancias que se registran pero nunca anulan el registerComponentCallbacks() cuando supongo que deberían haberlo hecho). Este ejemplo de código supone que solo el paquete .gms.ads es problemático, pero si encuentra otros que están causando una fuga similar, puede agregar esos paquetes a la lista también:

public class MyApplication extends Application {
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void registerComponentCallbacks(ComponentCallbacks callback) {
        super.registerComponentCallbacks(callback);
        ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.onComponentCallbacksRegistered(callback);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
        ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.onComponentCallbacksUnregistered(callback);
        super.unregisterComponentCallbacks(callback);
    }

    public void forceUnregisterComponentCallbacks() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.unregisterAll(this);
        }
    }


    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private static class ComponentCallbacksBehavioralAdjustmentToolIcs {
        static ComponentCallbacksBehavioralAdjustmentToolIcs INSTANCE = new ComponentCallbacksBehavioralAdjustmentToolIcs();

        private WeakHashMap<ComponentCallbacks, ApplicationErrorReport.CrashInfo> mCallbacks = new WeakHashMap<>();
        private boolean mSuspended = false;

        public void onComponentCallbacksRegistered(ComponentCallbacks callback) {
            Throwable thr = new Throwable("Callback registered here.");
            ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(thr);

            if (BuildConfig.DEBUG) Log.w(TAG, "registerComponentCallbacks: " + callback, thr);

            if (!mSuspended) {
                if (callback.getClass().getName().startsWith("com.google.android.gms.ads")) {
                    mCallbacks.put(callback, ci);
                }
                // TODO: other classes may still prove to be problematic?  For now, only watch for .gms.ads, since we know those are misbehaving
            } else {
                if (BuildConfig.DEBUG) Log.e(TAG, "ComponentCallbacks was registered while tracking is suspended!");
            }
        }

        public void onComponentCallbacksUnregistered(ComponentCallbacks callback) {
            if (!mSuspended) {
                if (BuildConfig.DEBUG) {
                    Log.i(TAG, "unregisterComponentCallbacks: " + callback, new Throwable());
                }

                mCallbacks.remove(callback);
            }
        }

        public void unregisterAll(Context context) {
            mSuspended = true;

            for (Map.Entry<ComponentCallbacks, ApplicationErrorReport.CrashInfo> entry : mCallbacks.entrySet()) {
                ComponentCallbacks callback = entry.getKey();
                if (callback == null) continue;

                if (BuildConfig.DEBUG) {
                    Log.w(TAG, "Forcibly unregistering a misbehaving ComponentCallbacks: " + entry.getKey());
                    Log.w(TAG, entry.getValue().stackTrace);
                }

                try {
                    context.unregisterComponentCallbacks(entry.getKey());
                } catch (Exception exc) {
                    if (BuildConfig.DEBUG) Log.e(TAG, "Unable to unregister ComponentCallbacks", exc);
                }
            }

            mCallbacks.clear();
            mSuspended = false;
        }
    }
}

Luego, en el método onPause() (o onDestroy() ) de mi forceUnregisterComponentCallbacks() , llamo a mi método forceUnregisterComponentCallbacks() :

@Override
public void onPause() {
    ((MyApplication) getApplicationContext()).forceUnregisterComponentCallbacks()
    super.onPause();
}

Tenga en cuenta que ComponentCallbacks se introdujo en ICS, por lo que si ve problemas en las versiones anteriores a ICS, entonces este no es el problema.

(También me doy cuenta de que esto no aborda el problema exacto identificado en el OP, ya que tiene que ver con bindService() , no con ComponentCallbacks . Pero nos salvó de una fuga de memoria bastante importante que nos obligó a desactivar AdMob por completo hasta una revisión podría ser lanzado.)

Estoy usando AdMob en un fragmento. A veces veo la siguiente pila

10-23 14:27:38.916: E/ActivityThread(21250): Activity com.applegrew.app.skywifiremote.MainActivity has leaked ServiceConnection com.google.android.gms.common.[email protected] that was originally bound here
10-23 14:27:38.916: E/ActivityThread(21250): android.app.ServiceConnectionLeaked: Activity com.applegrew.app.skywifiremote.MainActivity has leaked ServiceConnection com.google.android.gms.common.[email protected] that was originally bound here
10-23 14:27:38.916: E/ActivityThread(21250):    at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:979)
10-23 14:27:38.916: E/ActivityThread(21250):    at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:873)
10-23 14:27:38.916: E/ActivityThread(21250):    at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1690)
10-23 14:27:38.916: E/ActivityThread(21250):    at android.app.ContextImpl.bindService(ContextImpl.java:1673)
10-23 14:27:38.916: E/ActivityThread(21250):    at android.content.ContextWrapper.bindService(ContextWrapper.java:517)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.gms.ads.identifier.a.b(SourceFile:179)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.gms.ads.identifier.a.a(SourceFile:207)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.a.t.d(SourceFile:83)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.a.t.b(SourceFile:131)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.a.q.a(SourceFile:258)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.a.q.a(SourceFile:195)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.gms.ads.internal.k.a(SourceFile:76)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.gms.ads.internal.request.c.f_(SourceFile:99)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.gms.ads.internal.util.b.run(SourceFile:17)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.gms.ads.internal.util.d.call(SourceFile:29)
10-23 14:27:38.916: E/ActivityThread(21250):    at com.google.android.gms.ads.internal.util.e.call(SourceFile:49)
10-23 14:27:38.916: E/ActivityThread(21250):    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
10-23 14:27:38.916: E/ActivityThread(21250):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
10-23 14:27:38.916: E/ActivityThread(21250):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
10-23 14:27:38.916: E/ActivityThread(21250):    at java.lang.Thread.run(Thread.java:841)

Desde el seguimiento de la pila, parece que el origen de la fuga es el código de AdMob. Sin embargo, en mi fragmento tengo código para destruir la vista de AdMob cuando se destruye el fragmento.

Fragmento de mi fragmento.

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    initAd();
}

private void initAd() {
    mAdView = (AdView) getView().findViewById(R.id.remote_pager_ad);
    if (mAdView != null) {
        AdRequest adRequest = new AdRequest.Builder().addTestDevice(
                AdRequest.DEVICE_ID_EMULATOR).build();
        mAdView.loadAd(adRequest);
    }
}

@Override
public void onPause() {
    mAdView.pause();
    super.onPause();
}

@Override
public void onResume() {
    super.onResume();
    mAdView.resume();
}

@Override
public void onDestroy() {
    mAdView.destroy();
    super.onDestroy();
}

Se ve como si necesita cancelar el registro del Servicio antes de que su Actividad perdiera el contexto. Este es el mismo problema para Dialog cuando llamas para despedir después de que la actividad ha perdido. : - /





admob