Fragment中的内存泄漏

时间:2015-05-09 19:59:43

标签: android memory-leaks leakcanary

我正在使用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

这似乎是难以捉摸的。我设置Interface以在FragmentActivity之间进行通信。我在mCoordinator中设置了此Interface onAttach()变量,然后我意识到我没有在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 个答案:

答案 0 :(得分:9)

documentation表示即使状态为“连接”,也可以安全地拨打connect()。或者"连接"。它还表示无论连接状态如何,您都可以安全地呼叫disconnect()。因此,我会删除" if"围绕调用connect()disconnect()的陈述。但是,我怀疑这会导致这个"泄漏"走开。

很明显,GamesClientImplActivity的引用存储为Context。我想这是在你GoogleApiClient打电话时GoogleApiClient.Builder.build()的构造中出现的情况。在GoogleApiClient完成后,Activity的实例仍然存在,这似乎是一个错误。但是,如果您应该connect()中的onStart()disconnect()中的onStop(),这似乎意味着您可以重复使用该连接(因为onStart()和{可以重复调用{1}}。要实现此目的,onStop() 必须保留对您GoogleApiClient的引用,即使您已拨打Context

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

GoogleApiClient

答案 1 :(得分: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时必须使用Application Context,否则会出现以下泄漏:

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

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

答案 2 :(得分:0)

在某些情况下it might not be called,您不应该依赖onDestroy()回调执行。更可靠的解决方案是将您的注册/取消注册代码放在onResume() / onPause()内。

片段onDetach()

Same goes(当然是另一个原因),将您的合理代码移至onStop()onPause()

答案 3 :(得分: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);
    }
}