ErQi

ConnectivityManager内存泄露

ConnectivityManager内存泄露,一脸懵逼.

ConnectivityManager内存泄露

在APP中使用了ConnectivityManager来进行网络判断,然后竟然莫名其妙的内存泄露了…一脸懵逼.

思路源于ConnectivityManager内存泄露分析

觉得直接看一遍作用不大,于是自己跟着源码来进行一次分析

Context

1
2
// 内存泄露代码
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

首先ActivityContext都知道其实是一个ContextImpl对象.那么调用的自然也就是ContextImplgetSystemService(String name)方法.
代码如下

1
2
3
4
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}

很明显这里调用了静态类的静态方法,进而返回了ConnectivityManager对象,进而导致内存泄露.

SystemServiceRegistry

很明显就是在SystemServiceRegistry调用静态方法中出现了问题.该方法调用了静态对象进行查找并返回,代码如下:

1
2
3
4
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}

方法中先是进行查到,返回一个ServiceFetcher对象,并且调用其getService(ContextImpl ctx)方法,来返回用到的ConnectivityManager对象.
SYSTEM_SERVICE_FETCHERS初始化是在静态语句块中完成的.通过Context.CONNECTIVITY_SERVICE在静态语句块中找到了其注册过程.代码如下:

1
2
3
4
5
6
7
8
registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
new StaticOuterContextServiceFetcher<ConnectivityManager>() {
@Override
public ConnectivityManager createService(Context context) {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
return new ConnectivityManager(context, service);
}});

StaticOuterContextServiceFetcher

由此可以看到在SystemServiceRegistry查找的方法中返回的是一个StaticOuterContextServiceFetcher对象.我感觉真相理我不远了(看了一遍别人分析的也好意思说...)
StaticOuterContextServiceFetcher对象代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static abstract class StaticOuterContextServiceFetcher<T> implements ServiceFetcher<T> {
private T mCachedInstance;
@Override
public final T getService(ContextImpl ctx) {
synchronized (StaticOuterContextServiceFetcher.this) {
if (mCachedInstance == null) {
mCachedInstance = createService(ctx.getOuterContext());
}
return mCachedInstance;
}
}
public abstract T createService(Context applicationContext);
}

这里就很清晰了,在调用getService(ContextImpl ctx)时候调用了createService(Context applicationContext)方法,传递过去的Context对象为ContextImpl.getOuterContext()返回的mOuterContext对象,而mOuterContext对象是在ContextImpl创建的时候进行的初始化mOuterContext = this;,也就是其本身.
再看SystemServiceRegistry中实现的createService()方法.它利用Context创建并返回了一个ConnectivityManager对象

ConnectivityManager

1
2
3
4
5
6
7
private static ConnectivityManager sInstance;
.....
public ConnectivityManager(Context context, IConnectivityManager service) {
mContext = checkNotNull(context, "missing context");
mService = checkNotNull(service, "missing IConnectivityManager");
sInstance = this;
}

这里可以看到ConnectivityManager是个单例对象,并且持有了Context的对象引用,即ContextImplmOuterContext对象,也就是自己.也就是ActivityContext.
到这里就水落石出了,坑爹的ConnectivityManager竟然是单例对象,还会持有调用对象的Context对象,我傻傻的用Activity去获取,自然导致Activity内存泄露,无法被释放.

解决办法

单例对象自然寿与天齐,Activity肯定是不能真么干的,那么就给他寿与天齐的Context对象即可.

1
context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);