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);