getSystemService()引起的内存泄漏
通常获取系统服务通过Context的getSystemService(String name)方法,举个例子:
//获取ConnectivityManager,这里的Context是Activity
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
每个系统服务都有独一无二的名称对应,其中CONNECTIVITY_SERVICE是定义在Context中的"connectivity":
/**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.ConnectivityManager} for handling management of
* network connections.
*
* @see #getSystemService(String)
* @see android.net.ConnectivityManager
*/
public static final String CONNECTIVITY_SERVICE = "connectivity";
1、getSystemService方法
getSystemService(String name)方法是定义在Context中的抽象方法:
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
在ContextImpl类中实现了getSystemService(String name)方法,关于Context极其各种子类在《各种Context及其初始化流程》一文中有详细的剖析。
@Override
public Object getSystemService(String name) {
if (vmIncorrectContextUseEnabled()) {
// Check incorrect Context usage.
if (WINDOW_SERVICE.equals(name) && !isUiContext()) {
final String errorMessage = "Tried to access visual service "
+ SystemServiceRegistry.getSystemServiceClassName(name)
+ " from a non-visual Context:" + getOuterContext();
final String message = "WindowManager should be accessed from Activity or other "
+ "visual Context. Use an Activity or a Context created with "
+ "Context#createWindowContext(int, Bundle), which are adjusted to "
+ "the configuration and visual bounds of an area on screen.";
final Exception exception = new IllegalAccessException(errorMessage);
StrictMode.onIncorrectContextUsed(message, exception);
Log.e(TAG, errorMessage + " " + message, exception);
}
}
//通过SystemServiceRegistry拿到对应的Service
return SystemServiceRegistry.getSystemService(this, name);
}
接下来进入到SystemServiceRegistry类中,从类名不难看出这个类负责系统服务注册。SystemServiceRegistry类中的getSystemService(ContextImpl ctx, String name)实现如下:
/**
* Gets a system service from a given context.
* @hide
*/
public static Object getSystemService(ContextImpl ctx, String name) {
if (name == null) {
return null;
}
final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
if (fetcher == null) {
if (sEnableServiceNotFoundWtf) {
Slog.wtf(TAG, "Unknown manager requested: " + name);
}
return null;
}
final Object ret = fetcher.getService(ctx);
if (sEnableServiceNotFoundWtf && ret == null) {
// Some services do return null in certain situations, so don't do WTF for them.
switch (name) {
case Context.CONTENT_CAPTURE_MANAGER_SERVICE:
case Context.APP_PREDICTION_SERVICE:
case Context.INCREMENTAL_SERVICE:
case Context.ETHERNET_SERVICE:
return null;
}
Slog.wtf(TAG, "Manager wrapper not available: " + name);
return null;
}
return ret;
}
通过服务name从SYSTEM_SERVICE_FETCHERS拿到ServiceFetcher,进而通过ServiceFetcher获取对应的系统服务。这个SYSTEM_SERVICE_FETCHERS想必不看也应该知道是一个 系统服务名 到 系统服务 的映射:
private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new ArrayMap<String, ServiceFetcher<?>>();
2、系统服务注册
让我们来看看系统服务在哪里注册到SYSTEM_SERVICE_FETCHERS映射中的。找到这个registerService()方法,系统服务通过此方法注册到SYSTEM_SERVICE_FETCHERS中。
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
*/
private static <T> void registerService(@NonNull String serviceName,
@NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
}
在SystemServiceRegistry类中有长达一千多行的静态代码块,当SystemServiceRegistry类首次被虚拟机加载的时候就会执行静态代码块中的代码,把系统服务注册添加到集合SYSTEM_SERVICE_FETCHERS 中,后面使用的时候通过getSystemService()从集合中去获取。
static {
//CHECKSTYLE:OFF IndentationCheck
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher<AccessibilityManager>() {
@Override
public AccessibilityManager createService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});
...
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
// modules.
ConnectivityFrameworkInitializer.registerServiceWrappers();
JobSchedulerFrameworkInitializer.registerServiceWrappers();
BlobStoreManagerFrameworkInitializer.initialize();
TelephonyFrameworkInitializer.registerServiceWrappers();
AppSearchManagerFrameworkInitializer.initialize();
WifiFrameworkInitializer.registerServiceWrappers();
StatsFrameworkInitializer.registerServiceWrappers();
RollbackManagerFrameworkInitializer.initialize();
MediaFrameworkPlatformInitializer.registerServiceWrappers();
MediaFrameworkInitializer.registerServiceWrappers();
RoleFrameworkInitializer.registerServiceWrappers();
SchedulingFrameworkInitializer.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
sInitializing = false;
}
}
这里以ConnectivityManager服务注册为例,它的注册也比其它服务特殊一些,并没有直接在SystemServiceRegistry中注册,而是通过ConnectivityFrameworkInitializer中的registerServiceWrappers()方法注册。ConnectivityFrameworkInitializer位于packages/modules/Connectivity/framework/src/android/net目录中。
/**
* Called by {@link SystemServiceRegistry}'s static initializer and registers all core
* connectivity services to {@link Context}, so that {@link Context#getSystemService} can
* return them.
*
* @throws IllegalStateException if this is called anywhere besides
* {@link SystemServiceRegistry}.
*/
public static void registerServiceWrappers() {
// registerContextAwareService will throw if this is called outside of SystemServiceRegistry
// initialization.
SystemServiceRegistry.registerContextAwareService(
Context.CONNECTIVITY_SERVICE,
ConnectivityManager.class,
(context, serviceBinder) -> {
IConnectivityManager icm = IConnectivityManager.Stub.asInterface(serviceBinder);
return new ConnectivityManager(context, icm);
}
);
SystemServiceRegistry.registerContextAwareService(
Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
ConnectivityDiagnosticsManager.class,
(context) -> {
final ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
return cm.createDiagnosticsManager();
}
);
SystemServiceRegistry.registerContextAwareService(
Context.TEST_NETWORK_SERVICE,
TestNetworkManager.class,
context -> {
final ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
return cm.startOrGetTestNetworkManager();
}
);
SystemServiceRegistry.registerContextAwareService(
DnsResolverServiceManager.DNS_RESOLVER_SERVICE,
DnsResolverServiceManager.class,
(context, serviceBinder) -> new DnsResolverServiceManager(serviceBinder)
);
}
在ConnectivityFrameworkInitializer中的registerServiceWrappers()方法中又回到了SystemServiceRegistry,最终通过SystemServiceRegistry#registerContextAwareService()方法注册ConnectivityManager:
@SystemApi
public static <TServiceClass> void registerContextAwareService(
@NonNull String serviceName, @NonNull Class<TServiceClass> serviceWrapperClass,
@NonNull ContextAwareServiceProducerWithBinder<TServiceClass> serviceProducer) {
ensureInitializing("registerContextAwareService");
Preconditions.checkStringNotEmpty(serviceName);
Objects.requireNonNull(serviceWrapperClass);
Objects.requireNonNull(serviceProducer);
registerService(serviceName, serviceWrapperClass,
new CachedServiceFetcher<TServiceClass>() {
@Override
public TServiceClass createService(ContextImpl ctx)
throws ServiceNotFoundException {
return serviceProducer.createService(
//传入Context中的mOuterContext
ctx.getOuterContext(),
ServiceManager.getServiceOrThrow(serviceName));
}});
}
3、创建系统服务
注意,在第2节最后给用来创建系统服务的createService()方法传入的是ContextImpl中的mOuterContext:
@UnsupportedAppUsage
final void setOuterContext(@NonNull Context context) {
mOuterContext = context;
}
@UnsupportedAppUsage
final Context getOuterContext() {
return mOuterContext;
}
那么ContextImpl中的mOuterContext什么时候被设置的呢?以Activity为例,在Activity启动流程一文中,知道Activity在performLaunchActivity()方法中被创建:
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//创建ContextImpl
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
...
//并且将当前创建的Activity 作为OuterContext设置给ContextImpl
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
...
return activity;
}
其中通过createBaseContextForActivity(r)方法创建ContextImpl实例:
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
final int displayId = ActivityClient.getInstance().getDisplayId(r.token);
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
// The rotation adjustments must be applied before creating the activity, so the activity
// can get the adjusted display info during creation.
if (r.mPendingFixedRotationAdjustments != null) {
// The adjustments should have been set by handleLaunchActivity, so the last one is the
// override for activity resources.
if (mActiveRotationAdjustments != null && !mActiveRotationAdjustments.isEmpty()) {
mResourcesManager.overrideTokenDisplayAdjustments(r.token,
mActiveRotationAdjustments.get(
mActiveRotationAdjustments.size() - 1).second);
}
r.mPendingFixedRotationAdjustments = null;
}
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// For debugging purposes, if the activity's package name contains the value of
// the "debug.use-second-display" system property as a substring, then show
// its content on a secondary display if there is one.
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
break;
}
}
}
return appContext;
}
同样在performLaunchActivity()中将当前启动的Activity设置给刚创建好的ContextImpl:
...
appContext.setOuterContext(activity);
...
回到最初的getSystemService(ContextImpl ctx, String name)方法中,通过ServiceFetcher获取Service,使用的构造正是从ConnectivityFrameworkInitializer传入的:
SystemServiceRegistry.registerContextAwareService(
Context.CONNECTIVITY_SERVICE,
ConnectivityManager.class,
(context, serviceBinder) -> {
IConnectivityManager icm = IConnectivityManager.Stub.asInterface(serviceBinder);
return new ConnectivityManager(context, icm);
}
);
那么为什么可能会导致内存泄漏呢?还是以创建ConnectivityManager实例为例,ConnectivityManager的构造方法如下:
/**
* {@hide}
*/
public ConnectivityManager(Context context, IConnectivityManager service) {
this(context, service, true /* newStatic */);
}
紧接着调用ConnectivityManager的构造方法,将传入的Context参数保存给内部mContext成员,通过前面的分析,传进来的可能是mOuterContext,也就是Activity对象。
/**
* {@hide}
*/
public ConnectivityManager(Context context, IConnectivityManager service) {
mContext = Objects.requireNonNull(context, "missing context");
mService = Objects.requireNonNull(service, "missing IConnectivityManager");
sInstance = this;
}
频繁创建系统服务非常消耗资源,所以系统服务通常都是单例。而这个静态单例如果持有了应用的Activity就会造成内存泄漏,这下明白了吧?隐藏的很深的内存泄漏!
4、解决内存泄漏
去review了一下AOSP最新的代码,可以看到Google已经在2022年5月3号的I87206e1bfbb1f877b5a10f5fdbc25e2f9f11bef4这笔提交中处理了这个内存泄漏:
修改为使用getApplicationContext()作为引用的Context:
要等各手机厂商系统同步AOSP的这笔修改还需要一段时间,最简单的解决办法就是上面的方法,先getApplicationContext()再去调用getSystemService(String name)方法获取系统服务,而不应直接通过Activity或者Service作为Context去获取系统服务。
//使用getApplicationContext()拿到应用Context再去获取系统服务
ConnectivityManager connMgr = activity.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);