android - 是什么导致 Google AdMob 泄露 ServiceConnection?

标签 android android-fragments admob

我在 fragment 中使用 AdMob。有时我会看到以下堆栈

10-23 14:27:38.916: E/ActivityThread(21250): Activity com.applegrew.app.skywifiremote.MainActivity has leaked ServiceConnection com.google.android.gms.common.b@420e82e8 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.b@420e82e8 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)

从堆栈跟踪来看,泄漏源似乎是 AdMob 代码。但是,在我的 fragment 中,我有代码可以在 fragment 被销毁时销毁 AdMob View 。

我的 fragment 中的 fragment 。

@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();
}

最佳答案

我在 AdMob(Google Play 服务)广告泄漏大量内存时遇到了类似的问题。使用 MAT 发现问题在于我的 Application 实例在 sComponentCallbacks 数组中保留了每个 gms 广告。所以我重写了 registerComponentCallbacks()unregisterComponentCallbacks() 以跟踪正在注册自己但从不取消注册的实例(当我认为他们应该这样做时)。此代码示例假设只有 .gms.ads 包有问题,但如果您发现其他导致类似泄漏的包,您也可以将这些包添加到列表中:

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

然后在我的 BaseActivity 的 onPause()(或 onDestroy())方法中,我调用我的 forceUnregisterComponentCallbacks() 方法:

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

请注意,在 ICS 中引入了 ComponentCallbacks,因此如果您在 ICS 之前的版本上看到问题,那么这不是问题。

(我也意识到这并没有解决 OP 中确定的确切问题,因为这与 bindService() 相关,而不是 ComponentCallbacks。但它保存了我们从一个相当严重的内存泄漏中解脱出来,这迫使我们完全禁用 AdMob,直到可以发布一个修补程序。)

关于android - 是什么导致 Google AdMob 泄露 ServiceConnection?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26525185/

相关文章:

android - Anko - 自定义警报未显示

android - 将背景图像添加到各个 fragment

android - Admob 中的激励视频广告 - 中介不起作用

安卓 JSON 错误 "Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2"

java - 始终在 Android 中显示 map 标记标题

android - HTML5 移动应用程序是否在服务器上运行并通过各种网络浏览器访问?

android - getSupportFragmentManager 不在 DialogFragment 上编译

java - 无法从父 View 调用 fragment 的方法

ios - 如何从应用代理中删除插页式广告 - 应用内购买

android - Admob 横幅广告未在 Android 中显示