kingfeng's Notes

ServiceConnection 造成的内存泄漏

内存泄露日志

在项目中使用LeakCanary进行内存泄漏收集的时候出现了下面的一个memory leak:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
06-23 11:23:08.512 10127-19987/org.xxx.xxx D/LeakCanary: In org.kingfeng.xxx:3.6.0:27.
* org.kingfeng.xxx.activity.XXXActivity has leaked:
* GC ROOT static org.xxx.xxx.application.MyApplication.myApplication
* references org.xxx.xxx.application.MyApplication.mLoadedApk
* references android.app.LoadedApk.mServices
* references java.util.HashMap.table
* references array java.util.HashMap$HashMapEntry[].[0]
* references java.util.HashMap$HashMapEntry.value
* references java.util.HashMap.table
* references array java.util.HashMap$HashMapEntry[].[0]
* references java.util.HashMap$HashMapEntry.key
* references org.xxx.xxx.activity.XXXActivity$5.this$0 (anonymous class implements android.content.ServiceConnection)
* leaks org.1.xxx.activity.XXXActivity instance
* Reference Key: 9e535d7a-b609-40f0-9c45-2e0f56711180
* Device: HUAWEI Huawei MediaPad T1 8.0 msm8610
* Android Version: 4.3 API: 18
* Durations: watch=5028ms, gc=223ms, heap dump=1182ms, analysis=37294ms

功能实现

在activity中有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if(service instanceof MyService.MyBinder) {
myService = ((MyService.MyBinder) service).getService();
myService.registerConnectionStatusCallback(XXXActivity.this);
}
}

@Override
public void onServiceDisconnected(ComponentName name) {
myService.unRegisterConnectionStatusCallback();
myService = null;
}
};

问题原因及解决思路

看了源码中 onServiceDisconnected(ComponentName name)方法注释:

1
2
3
4
5
6
7
8
9
/**
* Called when a connection to the Service has been lost. This typically
* happens when the process hosting the service has crashed or been killed.
* This does <em>not</em> remove the ServiceConnection itself -- this
* binding to the service will remain active, and you will receive a call
* to {@link #onServiceConnected} when the Service is next running.
*
* @param name The concrete component name of the service whose connection has been lost.
*/

意思就是通常情况下,在托管这个service的进程崩溃或者被杀死的情况下,这个方法才会调用。
也就是只是Activity销毁,这个方法并不会调用。导致connection持有 XXXActivity的引用,不能及时被GC回收,造成内存泄漏。
搞了好大一会儿才把这个问题解决掉,做个记录。

解决办法
ActivityonDestroy() 中:

1
2
3
4
5
6
7
@Override
protected void onDestroy() {
super.onDestroy();
if(myService != null) {
myService.unRegisterConnectionStatusCallback();
}
}



参考:
Android中Service的使用详解和注意点

您的支持将鼓励我继续创作