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

2020-06-08  本文已影响0人  杨0612
应用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变量是如何实例化的。

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

上一篇下一篇

猜你喜欢

热点阅读