美文网首页
源码分析->一个应用到底有几个Context

源码分析->一个应用到底有几个Context

作者: 杨0612 | 来源:发表于2020-06-08 15:05 被阅读0次
    应用Context实例个数= Activity个数 +Service个数 + Application个数

    相信很多人都知道是这样计算的,那到底为什么是这样呢?

    源码分析基于Android28源码

    Context

    什么是Context呢?可以理解为上下文、运行环境,当需要获取资源、系统服务以及启动Activity、Service用到,也可以通过它跟系统交互。

    Activity

    通过以下继承关系可以看出,Activity是继承ContextWrapper


    Activity继承关系.png

    ContextWrapper内部有一个Context类型的成员变量mBase

    public class ContextWrapper extends Context {
        Context mBase;
        public ContextWrapper(Context base) {
            mBase = base;
        }
        ......
    

    mBase是通过attachBaseContext()方法赋值

        protected void attachBaseContext(Context base) {
            if (mBase != null) {
                throw new IllegalStateException("Base context already set");
            }
            mBase = base;
        }
    
    AMS.performLaunchActivity

    是创建Activity的关键,
    主要工作
    (1)createBaseContextForActivity()内部实例化ContextImpl 对象;
    (2)mInstrumentation.newActivity()内部通过反射实例化Activity对象;
    (3)activity.attach()内部会调用attachBaseContext()方法给mBase对象赋值;

        private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            ......
            ContextImpl appContext = createBaseContextForActivity(r);
            Activity activity = null;
            try {
                java.lang.ClassLoader cl = appContext.getClassLoader();
                activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
              ......
                    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);
              ......
    
    Application

    通过以下继承关系可以看出,Application是继承ContextWrappe


    Application继承关系.png
    LoadedApk.makeApplication

    是创建Application的关键,
    主要工作:
    (1)ContextImpl.createAppContext()实例化ContextImpl ;
    (2)mActivityThread.mInstrumentation.newApplication(),内部通过反射实例化Application,并把appContext传递过去,通过attach()方法给mBase赋值;

        public Application makeApplication(boolean forceDefaultAppClass,
                Instrumentation instrumentation) {
                 ......     
                ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
                app = mActivityThread.mInstrumentation.newApplication(
                        cl, appClass, appContext);
                ......
    
    Service

    跟Activity类似就不再做分析。
    经过分析发现:
    1.每个Activity,Service,Application都有一个ContextImpl 类型的成员变量mBase,ContextImpl是Context的实现类。
    2.细心的读者可能发现,Activity,Service,Application都是继承Context,其实他们本身是一个Context,也都实现了Context的抽象方法,
    那么一个Activity是否就拥有两个Context呢?
    是不是

    应用Context实例个数= (Activity个数 +Service个数 + Application个数)*2

    这样计算比较合适呢?

    下面看下Context中常用的三个方法,

         public AssetManager getAssets() ;
         public Resources getResources() ;
         public PackageManager getPackageManager();
    

    ContextImpl继承Context,并实现了这三个方法,

    class ContextImpl extends Context {
        final @NonNull ActivityThread mMainThread;//ActivityThread 对象
        final @NonNull LoadedApk mPackageInfo;//包安装信息类
    
        private final @NonNull ResourcesManager mResourcesManager;//资源管理
        private @NonNull Resources mResources;
        .......
        @Override
        public AssetManager getAssets() {
            return getResources().getAssets();
        }
       @Override
        public Resources getResources() {
            return mResources;
        }
    
        @Override
        public PackageManager getPackageManager() {
            if (mPackageManager != null) {
                return mPackageManager;
            }
    
            IPackageManager pm = ActivityThread.getPackageManager();
            if (pm != null) {
                // Doesn't matter if we make more than one instance.
                return (mPackageManager = new ApplicationPackageManager(this, pm));
            }
    
            return null;
        }
    

    Activity间接继承Context,主要是在ContextWrapper实现了以上三个方法,从源码中可以看出,最终还是调用了ContextImpl的实现。

       @Override
        public AssetManager getAssets() {
            return mBase.getAssets();
        }
       @Override
        public Resources getResources() {
            return mBase.getResources();
        }
        @Override
        public PackageManager getPackageManager() {
            return mBase.getPackageManager();
        }
    
    

    下图可以看出这几个的关系,ContextWrapper顾名思义就是Context的包装类(有ContextImpl的成员变量),并且实现了Context,这是一种装饰者设计模式。当在Activity中调用getAsset()时,其实最终是调用mBase的getAsset()。


    context继承关系.png
    结论

    Activity间接继承了Context,是为了拥有跟ContextImpl一样的功能,但真正起作用的是mBase这个成员变量,所以一个Activity其实就只有一个Context起作用,那就是ContextImpl类型的mBase。

    应用Context实例个数= Activity个数 +Service个数 + Application个数

    这种计算方法应该是没有问题呢。
    或许有人有这样的疑问,一个应用不是只有一个Application吗,为什么计算公式是加上Application个数?单进程应用来说,一个应用确实只有一个Application,而多进程应用,那么一个应用就有多个Application,所以应该说一个应用有一个或多个Application,一个进程有一个Application。

    补充一点:BroadcastReceiver是没有Context的,onReceiver传进来的Context是注册该广播的Context ,而ContentProvider的Context是Application的Context。

    另外其他关于Context的常见面试题
    1.Activity的this跟getBaseContext区别。
    前者是Activity对象本身,后者是通过attachBaseContext传入的ContextImpl对象mBase,两者功能是一样的,通过this最终还是会调到mBase对象中。
    2.getApplication和geApplicationContext区别。
    两者都是返回Application对象,前者是Activity和Service里面的方法,后者是Context中定义的方法。
    3.应用组件的构造,onCreate、attachBaseContext的执行顺序。
    先是组件构造化,接着attachBaseContext,传入ContextImpl对象,最后是onCreate方法。
    4.谈谈你对Context的理解
    先是Context的作用,然后是有几种Context,Application、Service、Activity的Context有什么区别以及继承关系,
    最后是mBase变量是如何实例化的。

    以上分析有不对的地方,请指出,互相学习,谢谢哦!

    相关文章

      网友评论

          本文标题:源码分析->一个应用到底有几个Context

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