20

我正在使用LeakCanary库来监控我的应用程序中的内存泄漏。我收到了这个内存泄漏,但不知道如何找出导致它的原因。

05-09 09:32:14.731  28497-31220/? D/LeakCanary﹕ In com.etiennelawlor.minesweeper:0.0.21:21.
    * com.etiennelawlor.minesweeper.fragments.MinesweeperFragment has leaked:
    * GC ROOT com.google.android.gms.games.internal.GamesClientImpl$PopupLocationInfoBinderCallbacks.zzahO
    * references com.google.android.gms.games.internal.PopupManager$PopupManagerHCMR1.zzajo
    * references com.google.android.gms.games.internal.GamesClientImpl.mContext
    * references com.etiennelawlor.minesweeper.activities.MinesweeperActivity.mFragments
    * references android.app.FragmentManagerImpl.mAdded
    * references java.util.ArrayList.array
    * references array java.lang.Object[].[0]
    * leaks com.etiennelawlor.minesweeper.fragments.MinesweeperFragment instance
    * Reference Key: 2f367393-6dfd-4797-8d85-7ac52c431d07
    * Device: LGE google Nexus 5 hammerhead
    * Android Version: 5.1 API: 22
    * Durations: watch=5015ms, gc=141ms, heap dump=1978ms, analysis=23484ms

这是我的回购: https ://github.com/lawloretienne/Minesweeper

这似乎是一个难以捉摸的事情。我设置了一个在 a和Interfacea 之间进行通信。我设置了这个变量,然后我意识到我没有在. 我解决了这个问题,但仍然出现内存泄漏。有任何想法吗?FragmentActivitymCoordinator InterfaceonAttach()onDetach()

更新

我禁用了Fragment泄漏监视,但我仍然收到有关活动泄漏的通知,其中包含以下泄漏跟踪:

05-09 17:07:33.074  12934-14824/? D/LeakCanary﹕ In com.etiennelawlor.minesweeper:0.0.21:21.
    * com.etiennelawlor.minesweeper.activities.MinesweeperActivity has leaked:
    * GC ROOT com.google.android.gms.games.internal.GamesClientImpl$PopupLocationInfoBinderCallbacks.zzahO
    * references com.google.android.gms.games.internal.PopupManager$PopupManagerHCMR1.zzajo
    * references com.google.android.gms.games.internal.GamesClientImpl.mContext
    * leaks com.etiennelawlor.minesweeper.activities.MinesweeperActivity instance
    * Reference Key: f4d06830-0e16-43a2-9750-7e2cb77ae24d
    * Device: LGE google Nexus 5 hammerhead
    * Android Version: 5.1 API: 22
    * Durations: watch=5016ms, gc=164ms, heap dump=3430ms, analysis=39535ms
4

4 回答 4

9

文档表明,connect()即使状态为“已连接”或“正在连接” ,调用也是安全的。它还表明disconnect()无论连接状态如何,您都可以安全地调用。因此,我将删除调用connect()and周围的“if”语句disconnect()。但是,我怀疑这会使这种“泄漏”消失。

很明显,它将GamesClientImpl对您的引用存储ActivityContext. GoogleApiClient我想这发生在你调用时发生的构造中GoogleApiClient.Builder.build()。对我来说,在你完成GoogleApiClient后实例仍然存在,这似乎是一个错误。但是,Activity如果您应该调用并且connect()in这似乎意味着您可以重用连接(因为并且可以重复调用)。为此,必须保留对您的引用,即使您已调用.onStart()disconnect()onStop()onStart()onStop()GoogleApiClient Contextdisconnect()

Activity您可以在创建时尝试使用全局应用程序上下文而不是您的上下文GoogleApiClient,因为全局应用程序上下文永远存在(直到进程被终止)。这应该使您的“泄漏”消失:

// Create the Google Api Client with access to Plus and Games
mGoogleApiClient = new GoogleApiClient.Builder(getApplicationContext())
    .addConnectionCallbacks(this)
    .addOnConnectionFailedListener(this)
    .addApi(Plus.API).addScope(Plus.SCOPE_PLUS_LOGIN)
    .addApi(Games.API).addScope(Games.SCOPE_GAMES)
    .build();
于 2015-05-15T17:09:00.527 回答
2
05-27 13:15:04.478  24415-25236/com.package D/LeakCanary﹕ In com.package:0.0.52-dev:202.
* com.package.launcher.LauncherActivity has leaked:
* GC ROOT com.google.android.gms.ads.internal.request.q.a
* references com.google.android.gms.ads.internal.request.m.d
* references com.google.android.gms.ads.internal.request.c.a
* references com.google.android.gms.ads.internal.j.b
* references com.google.android.gms.ads.internal.aa.f
* references com.google.android.gms.ads.internal.ab.mParent
* references com.google.android.gms.ads.doubleclick.PublisherAdView.mParent
* references android.widget.FrameLayout.mContext
* leaks com.package.launcher.LauncherActivity instance
* Reference Key: 9ba3c5ea-2888-4677-9cfa-ebf38444c994
* Device: LGE google Nexus 5 hammerhead
* Android Version: 5.1.1 API: 22
* Durations: watch=5128ms, gc=150ms, heap dump=5149ms, analysis=29741ms

我正在使用 gms 广告库,并且有类似的泄漏。所以我通过处理我的片段的 onDestroyView() 来修复上述情况。

@Override
public void onDestroyView() {

    if (mAdView != null) {
        ViewParent parent = mAdView.getParent();
        if (parent != null && parent instanceof ViewGroup) {
            ((ViewGroup) parent).removeView(mAdView);
        }
    }
    super.onDestroyView();
}

所以在这里我基本上是在 onDestroyView() 上从它的父级中删除我的 PublisherAdView。

另外,请注意,我在创建 PublisherAdView 时必须使用应用程序上下文,否则会出现以下泄漏:

05-27 13:59:23.684  10041-11496/com.package D/LeakCanary﹕ In com.package:0.0.52-dev:202.
* com.package.launcher.LauncherActivity has leaked:
* GC ROOT com.google.android.gms.ads.internal.request.q.a
* references com.google.android.gms.ads.internal.request.m.b
* leaks com.package.launcher.LauncherActivity instance
* Reference Key: 5acaa61a-ea04-430a-b405-b734216e7e80
* Device: LGE google Nexus 5 hammerhead
* Android Version: 5.1.1 API: 22
* Durations: watch=7275ms, gc=138ms, heap dump=5260ms, analysis=22447ms

不确定它是否会直接解决上面的问题,但希望它有所帮助。

于 2015-05-27T17:38:25.860 回答
0

您不应该依赖onDestroy()回调执行,在某些情况下它可能不会被调用。更可靠的解决方案是将您的注册/注销代码放在onResume()/onPause()中。

对于 Fragment's 也是如此(当然,出于另一个原因)onDetach(),将您的合理代码移至onStop()or onPause()

于 2015-05-12T12:12:28.867 回答
0

就我而言,我有以下代码:

googleApiClient = new GoogleApiClient.Builder(activity.getApplicationContext())
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(Games.API).addScope(Games.SCOPE_GAMES)
            .build();

问题是电话addConnectionCallbacks(this)addConnectionCallbacks(this)。引用的类this保留了对活动的引用,并且由于 GoogleApiClient 不会放弃对连接回调/侦听器的引用,这导致了内存泄漏。

我的解决方案是将回调注册/取消注册为googleApiClient连接/断开连接:

public void connect() {
    mGoogleApiClient.registerConnectionCallbacks(this);
    mGoogleApiClient.registerConnectionFailedListener(this);
    mGoogleApiClient.connect();
}

public void disconnect() {
    if (mGoogleApiClient.isConnected()) {
        mGoogleApiClient.disconnect();
        mGoogleApiClient.unregisterConnectionCallbacks(this);
        mGoogleApiClient.unregisterConnectionFailedListener(this);
    }
}
于 2017-01-19T12:57:03.137 回答