美文网首页
Context,ContextImpl等的一些理解

Context,ContextImpl等的一些理解

作者: 老码w | 来源:发表于2019-12-25 17:55 被阅读0次

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://www.jianshu.com/p/674682b1976f

    背景:我们经常在Activity中会得到一些系统服务或者得到Resources,比如ACTIVITY_SERVICE,常常获取的方式有好几种,如下:

    //得到ACTIVITY_SERVICE
    getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
    this.getSystemService(Context.ACTIVITY_SERVICE);
    getBaseContext().getSystemService(Context.ACTIVITY_SERVICE);
    //得到Resources
    getResources();
    getBaseContext().getResources();
    getApplicationContext().getResources();
    

    这时候就有一种疑问,最后返回的是同一个对象吗,如果不是,又会有什么差异。
    要想弄清上面的问题,先得明白Context,ContextImpl,ContextWrapper,ContextThemeWrapper,Activity,Application等等的关系。

    public abstract class Context {
            public abstract Resources getResources();
            public abstract Object getSystemService(@ServiceName @NonNull String name);
    }
    
    class ContextImpl extends Context {
        @Override
        public Object getSystemService(String name) {
            return SystemServiceRegistry.getSystemService(this, name);
        }
        @Override
        public Resources getResources() {
            return mResources;
        }
    }
    
    public class ContextWrapper extends Context {
        Context mBase;
        @Override
        public Resources getResources() {
            return mBase.getResources();
        }
        @Override
        public Object getSystemService(String name) {
            return mBase.getSystemService(name);
        }
    }
    
    public class ContextThemeWrapper extends ContextWrapper {
        @Override
        public Object getSystemService(String name) {
            if (LAYOUT_INFLATER_SERVICE.equals(name)) {
                if (mInflater == null) {
                    mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
                }
                return mInflater;
            }
            return getBaseContext().getSystemService(name);
        }
    }
    
    public class Activity extends ContextThemeWrapper{
    }
    

    从上面的代码可以知道,Context是一个抽象类,它的很多方法,比如getResources,getSystemService等都是抽象方法。ContextWrapper类和ContextImpl类都继承了Context,但是他们的实现方法不一样。ContextWrapper中主要是调用mBase对应的方法,而mBase也是一个Context对象。
    其实通过研读ActivityThread的performLaunchActivity(参考https://www.jianshu.com/p/76f94c6452c0)我们不难明白,Activity中对应的Context实例对象是其实都是ContextImpl对象,ContextImpl也是真正实现Context的唯一对象。且ContextWrapper中的mBase对象实际上也是ContextImpl。ContextWrapper顾名思义,它只是Context的一个包裹,最终调用到的也是ContextImpl中的实现。而Activity也继承了ContextWrapper,所以最后殊途同归,所有的调用最终调用到了ContextImpl中。
    那Application又合Context有什么关系呢,如下:

    public class Application extends ContextWrapper{
    }
    

    可以看到Application也继承ContextWrapper,ContextWrapper继承Context,所以通过调用getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);最终调用到的是Application中的mBase的对应的getSystemService方法,mBase它是Context对象,也是ContextImpl实例,那它是如何设置为ContextImpl实例的呢,Application中有一attach函数,参数为context,它的调用是LoadedApk中的makeApplication时调用的

        final void attach(Context context) {
            attachBaseContext(context);
            mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
        }
    
    public Application makeApplication(boolean forceDefaultAppClass,
                Instrumentation instrumentation) {
                Application app = null;
                ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
                app = mActivityThread.mInstrumentation.newApplication(
                        cl, appClass, appContext);
                appContext.setOuterContext(app);
    }
    
    public Application newApplication(ClassLoader cl, String className, Context context)
                throws InstantiationException, IllegalAccessException, 
                ClassNotFoundException {
            return newApplication(cl.loadClass(className), context);
        }
    
    static public Application newApplication(Class<?> clazz, Context context)
                throws InstantiationException, IllegalAccessException, 
                ClassNotFoundException {
            Application app = (Application)clazz.newInstance();
            app.attach(context);
            return app;
        }
    

    所以可以看到Application中最终也是调用到了ContextImpl中去。
    至此,我们明白了getSystemService,无论是Activity的直接getSystemService()调用,还是通过getBaseContext调用还是getApplicationContext()调用,最终都是调用到了ContextImpl中去。我们来看看具体的代码跟踪。
    getSystemService代码跟踪,Activity中有WINDOW_SERVICE和SEARCH_SERVICE的缓存,直接返回

    @Override
        public Object getSystemService(@ServiceName @NonNull String name) {
            if (getBaseContext() == null) {
                throw new IllegalStateException(
                        "System services not available to Activities before onCreate()");
            }
    
            if (WINDOW_SERVICE.equals(name)) {
                return mWindowManager;
            } else if (SEARCH_SERVICE.equals(name)) {
                ensureSearchManager();
                return mSearchManager;
            }
            return super.getSystemService(name);
        }
    //super.getSystemService(name)
    @Override
        public Object getSystemService(String name) {
            if (LAYOUT_INFLATER_SERVICE.equals(name)) {
                if (mInflater == null) {
                    mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
                }
                return mInflater;
            }
            return getBaseContext().getSystemService(name);
        }
    

    可以看到最后是调用到了getBaseContext()的getSystemService,getBaseContext返回的是mBase对象,mBase对象是ContextImpl(Context)对象。它是在Activity的attach的时候设置的(参考https://www.jianshu.com/p/76f94c6452c0

    final void attach(Context context, ActivityThread aThread,
                Instrumentation instr, IBinder token, int ident,
                Application application, Intent intent, ActivityInfo info,
                CharSequence title, Activity parent, String id,
                NonConfigurationInstances lastNonConfigurationInstances,
                Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                Window window) {
            attachBaseContext(context);
    }
    

    所以最终调用到了ContextImpl的getSystemService。
    在看ContextImpl的getSystemService之前,先来看看通过Application的调用

    //Activity中
    getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
    
    @Override
        public Context getApplicationContext() {
            return mBase.getApplicationContext();
        }
    

    我们知道Activity中的mBase是ContextImpl对象,那么它的getApplicationContext返回什么对象呢

    final LoadedApk mPackageInfo;
    @Override
        public Context getApplicationContext() {
            return (mPackageInfo != null) ?
                    mPackageInfo.getApplication() : mMainThread.getApplication();
        }
    

    mPackageInfo是一个LoadedApk对象,它返回的mApplication对象就是调用makeApplication生成的Application对象。(见上面讲解代码)

    private Application mApplication;
    Application getApplication() {
            return mApplication;
        }
    

    即也就是调用到了Application中的getSystemService。而Application继承ContextWrapper,所以又是调用到了mBase的getSystemService。mBase为ContextImpl对象,所以最终调用到了ContextImpl中去。
    下面我们来看看最终ContextImpl的调用

    @Override
        public Object getSystemService(String name) {
            return SystemServiceRegistry.getSystemService(this, name);
        }
    //SystemServiceRegistry中的getSystemService
    public static Object getSystemService(ContextImpl ctx, String name) {
            ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
            return fetcher != null ? fetcher.getService(ctx) : null;
        }
    

    最终就是调用到了SystemServiceRegistry中注册的系统服务了。具体可以参考SystemServiceRegistry的源代码。这个就是属于注册系统服务的范畴了。非本篇讨论范围。
    最后是不是会有一个疑问,一个应用中到底会有多少个ContextImpl对象实例,一个还是多个。如果是多个对象,如何保证调用的最后归一呢。多个对象岂不是会占用很多内存。
    说到这些,就不得不提ContextImpl中比较重要的两个成员变量了

    final ActivityThread mMainThread;
    final LoadedApk mPackageInfo;
    

    同时,我们看到ContextImpl中有很多创建Context相关的函数,比如

    static ContextImpl createSystemContext(ActivityThread mainThread) {
            LoadedApk packageInfo = new LoadedApk(mainThread);
            ContextImpl context = new ContextImpl(null, mainThread,
                    packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
            context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                    context.mResourcesManager.getDisplayMetrics());
            return context;
        }
    
        static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
            if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
            return new ContextImpl(null, mainThread,
                    packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
        }
    
        static ContextImpl createActivityContext(ActivityThread mainThread,
                LoadedApk packageInfo, IBinder activityToken, int displayId,
                Configuration overrideConfiguration) {
            if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
            return new ContextImpl(null, mainThread, packageInfo, activityToken, null, 0,
                    null, overrideConfiguration, displayId);
        }
    @Override
        public Context createApplicationContext(ApplicationInfo application, int flags)
                throws NameNotFoundException {
            LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                    flags | CONTEXT_REGISTER_PACKAGE);
            if (pi != null) {
                ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
                        new UserHandle(UserHandle.getUserId(application.uid)), flags,
                        mDisplay, null, Display.INVALID_DISPLAY);
                if (c.mResources != null) {
                    return c;
                }
            }
    
            throw new PackageManager.NameNotFoundException(
                    "Application package " + application.packageName + " not found");
        }
    @Override
        public Context createPackageContext(String packageName, int flags)
                throws NameNotFoundException {
            return createPackageContextAsUser(packageName, flags,
                    mUser != null ? mUser : Process.myUserHandle());
        }
    @Override
        public Context createConfigurationContext(Configuration overrideConfiguration) {
            if (overrideConfiguration == null) {
                throw new IllegalArgumentException("overrideConfiguration must not be null");
            }
    
            return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
                    mUser, mFlags, mDisplay, overrideConfiguration, Display.INVALID_DISPLAY);
        }
    ``
    可以看到ContextImpl是会创建很多次的,它是一个轻量级的对象,虽然ContextImpl会被创建多个,但是它的成员变量mMainThread和mPackageInfo的对象引用是唯一的。这也是一个android进程里面比较重要的两个类。也只会生成唯一的对象。从中我们也可以看到google工程师设计android框架的一些巧妙之处和原理,希望能从中学习一二。
    
    
    

    相关文章

      网友评论

          本文标题:Context,ContextImpl等的一些理解

          本文链接:https://www.haomeiwen.com/subject/hitcoctx.html