我正在使用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
以在Fragment
和Activity
之间进行通信。我在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
答案 0 :(得分:9)
documentation表示即使状态为“连接”,也可以安全地拨打connect()
。或者"连接"。它还表示无论连接状态如何,您都可以安全地呼叫disconnect()
。因此,我会删除" if"围绕调用connect()
和disconnect()
的陈述。但是,我怀疑这会导致这个"泄漏"走开。
很明显,GamesClientImpl
将Activity
的引用存储为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);
}
}