本文主要是介绍(七十七)getSystemService内存泄露探讨,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前言:看了下https://www.jianshu.com/p/5d96983fc6db 这篇文章,文中有提及Android N之前WiFiManager会长时间持有context不释放,导致内存泄露。Android N以后修改其中的asyncChannel为非static的就好了,感觉没说的很清楚。
1.getSystemService流程
1.1 ContextImpl
@Overridepublic Object getSystemService(String name) {return SystemServiceRegistry.getSystemService(this, name);}
1.2 SystemServiceRegistry
/*** Gets a system service from a given context.*/public static Object getSystemService(ContextImpl ctx, String name) {ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);return fetcher != null ? fetcher.getService(ctx) : null;}
这边其实是获取的先前的缓存,但是有个前提是ctx是一样的才可以,也就是说一个应用的两个activity传递各自的context是不会得到复用的,使用ApplicationContext才可以得到复用。
/*** Manages all of the system services that can be returned by {@link Context#getSystemService}.* Used by {@link ContextImpl}.*/
final class SystemServiceRegistry {private static final String TAG = "SystemServiceRegistry";// Service registry information.// This information is never changed once static initialization has completed.private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =new HashMap<Class<?>, String>();private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =new HashMap<String, ServiceFetcher<?>>();private static int sServiceCacheSize;// Not instantiable.private SystemServiceRegistry() { }static {registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,new CachedServiceFetcher<AccessibilityManager>() {@Overridepublic AccessibilityManager createService(ContextImpl ctx) {return AccessibilityManager.getInstance(ctx);}});registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,new CachedServiceFetcher<CaptioningManager>() {@Overridepublic CaptioningManager createService(ContextImpl ctx) {return new CaptioningManager(ctx);}});
...registerService(Context.WIFI_SERVICE, WifiManager.class,new CachedServiceFetcher<WifiManager>() {@Overridepublic WifiManager createService(ContextImpl ctx) throws ServiceNotFoundException {IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);IWifiManager service = IWifiManager.Stub.asInterface(b);return new WifiManager(ctx.getOuterContext(), service,ConnectivityThread.getInstanceLooper());}});
...
/*** Override this class when the system service constructor needs a* ContextImpl and should be cached and retained by that context.*/static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {private final int mCacheIndex;public CachedServiceFetcher() {mCacheIndex = sServiceCacheSize++;}@Override@SuppressWarnings("unchecked")public final T getService(ContextImpl ctx) {final Object[] cache = ctx.mServiceCache;synchronized (cache) {// Fetch or create the service.Object service = cache[mCacheIndex];if (service == null) {try {service = createService(ctx);cache[mCacheIndex] = service;} catch (ServiceNotFoundException e) {onServiceNotFound(e);}}return (T)service;}}public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;}
/*** Statically registers a system service with the context.* This method must be called during static initialization only.*/private static <T> void registerService(String serviceName, Class<T> serviceClass,ServiceFetcher<T> serviceFetcher) {SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);}
关键代码都贴出来了
- 首先SystemServiceRegistry调用之初有个static的代码块,里面会将很多service的serviceFetcher存进static集合SYSTEM_SERVICE_FETCHERS中。
- 之后有调用getSystemService,从static集合SYSTEM_SERVICE_FETCHERS 中取出对应service的serviceFetcher,接着调用getService方法。
- getService获取的服务是先查询context中的mServiceCache,如果没有service的cache,则重新create一下。
- 重新create就分情况了,如果是AccessibilityManager这样的单例的manager对象还好,不会持有传入的context引用以存到context的mServiceChache中;如果是WifiManager,则传入多少个不同的context,那么就有多少个context的mServiceCache中包含WifiManager,如果WifiManager不释放,那么就导致内存泄露了。
PS:不同的contextImpl都会从SystemServiceRegistry中new出一个新的Object数组,是不共享的。
ContextImpl.java// The system service cache for the system services that are cached per-ContextImpl.final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();SystemServiceRegistry.java/*** Creates an array which is used to cache per-Context service instances.*/public static Object[] createServiceCache() {return new Object[sServiceCacheSize];}
2. 总结
一个应用的static变量还是和applicationContext更配一点,毕竟生命周期差不多,如果调用getSystemService,也不用管manager是单例的还是重新new的,传入applicationContext比较好。
3.疑问
getOutContext和普通的ctx有什么区别?是applicationContext么?
registerService(Context.DISPLAY_SERVICE, DisplayManager.class,new CachedServiceFetcher<DisplayManager>() {@Overridepublic DisplayManager createService(ContextImpl ctx) {return new DisplayManager(ctx.getOuterContext());}});
看了下OuterContext是在activity创建的时候初始化的,就是activity,appContext对应与activity 的服务端contextImpl。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");ActivityInfo aInfo = r.activityInfo;if (r.packageInfo == null) {r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,Context.CONTEXT_INCLUDE_CODE);}ComponentName component = r.intent.getComponent();if (component == null) {component = r.intent.resolveActivity(mInitialApplication.getPackageManager());r.intent.setComponent(component);}if (r.activityInfo.targetActivity != null) {component = new ComponentName(r.activityInfo.packageName,r.activityInfo.targetActivity);}ContextImpl appContext = createBaseContextForActivity(r);Activity activity = null;try {java.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);r.intent.prepareToEnterProcess();if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to instantiate activity " + component+ ": " + e.toString(), e);}}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);if (localLOGV) Slog.v(TAG, "Performing launch of " + r);if (localLOGV) Slog.v(TAG, r + ": app=" + app+ ", appName=" + app.getPackageName()+ ", pkg=" + r.packageInfo.getPackageName()+ ", comp=" + r.intent.getComponent().toShortString()+ ", dir=" + r.packageInfo.getAppDir());if (activity != null) {CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());Configuration config = new Configuration(mCompatConfiguration);if (r.overrideConfig != null) {config.updateFrom(r.overrideConfig);}if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "+ r.activityInfo.name + " with config " + config);Window window = null;if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {window = r.mPendingRemoveWindow;r.mPendingRemoveWindow = null;r.mPendingRemoveWindowManager = null;}appContext.setOuterContext(activity);
。。。
搜了下contextImpl初始化的时候会将outerContext初始化为自己,此外还有如下会重新赋值:
./base/core/java/android/app/LoadedApk.java:998: appContext.setOuterContext(app);
./base/core/java/android/app/ActivityThread.java:2780: appContext.setOuterContext(activity);
./base/core/java/android/app/ActivityThread.java:3371: context.setOuterContext(agent);
./base/core/java/android/app/ActivityThread.java:3445: context.setOuterContext(service);
./base/core/java/android/app/ContextImpl.java:2404: final void setOuterContext(Context context) {
这篇关于(七十七)getSystemService内存泄露探讨的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!