likes
comments
collection
share

一文搞懂 Android Context 上下文,不懂来打我

作者站长头像
站长
· 阅读数 30

Context 上下文是 Android 常用类,4大组件均涉及到 Context 的身影,今天来分析下 Context的设计思想。

1、Context 上下文介绍

Context 作为抽象类,在 Android 系统中有两大子类 ContextImpl 和 ContextWrapper;

  • ContextImpl 是 Context 真正实现类;
  • ContextWrapper 及其子类是装饰类;

我们熟知的 Application、Service、ContextThemeWraaper 等均是 ContextWrapper 的子类,其中 Activity 较为特殊因为其需要向用户展示UI,所以需要具备特定的 Theme 能力,因此 Activity 是 ContextThemeWrapper 的子类。 ContentProvider 通过组合方式,内部持有着 ContextWrapper; BroadcastReceiver 则通过 ContextWrapper 的子类 ReceiverRestrictedContext 间接持有 Application 的 context;

ContextWrapper 的变量 mBase 引用着 ContextImpl 对象。 其对 Context 真正的实现类 ContextImpl 进行装饰,可以增加上下文的业务,比如 Activity 扩展的主题相关业务;

Context的关联类采用了装饰模式,主要有以下的优点:·

  • 使用者(Activity)更方便使用Context;
  • ContextImpl 实现方式的修改,装饰类不需要做修改;
  • ContextImpl 不需要暴露给使用者,使用者也不需要关心具体实现,符合迪米特法则;
  • 通过组合而非继承方式扩展 ContextImpl 功能,可以在运行时选择不同的装饰类实现功能的扩展(ContextThemeWrapper 装饰类通过对 Theme 的扩展,实现了支持主题的 Context 上下文能力),符合开闭原则;

一文搞懂 Android Context 上下文,不懂来打我

我们在开发过程中,Activity、Service、Application 内使用的 Context 的方法,实际是调用的 ContextWrapper 的方法,ContextWrapper 是个空壳,内部通过 mBase 将调用转到 ContextImpl,因此与上下文相关的操作实际上是在 ContextImpl 内进行的。

如何使用Context不是本文重点,我们主要分析下各种组件的 Context 是如何创建的?

2、创建 Context 上下文

2.1、创建 Application Context

    // ActivityThread.java
    // 首次启动 Service/Activity/ContentProvider 时,需要先创建 Application
    private void handleCreateService(CreateServiceData data) {
        LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
        ...
        Application app = packageInfo.makeApplication(false, mInstrumentation);
    }


    // LoadedApk.java
    // 1、创建 Application
    public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }
        ...
        // 2、创建 Application 级别的 ContextImpl
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        // 3/4、创建 Application,并将 ContextImpl 绑定到 ContextWrapper(Application)  mBase 变量
        Application app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
        // 5、ContextImpl 将外部组件 Application 绑定到 mOuterContext 变量
        appContext.setOuterContext(app);
        ...
        return app;
    }


    // ContextImpl.java
    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
            String opPackageName) {
        // 创建 ContextImpl
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
            ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);
        return context;
    }


    // ContextImpl 绑定的外部组件
    private Context mOuterContext;
    final void setOuterContext(@NonNull Context context) {
        // 将 Application 绑定到 mOuterContext 变量
        mOuterContext = context;
        ...
    }
    final Context getOuterContext() {
        return mOuterContext;
    }


    // Instrumentation.java
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        // 3、构建 Application 对象
        Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);
        // 4、将 ContextImpl 绑定到 ContextWrapper(Application) 的 mBase 变量
        app.attach(context);
        return app;
    }


    // Application.java
    // super 方法传null,mBase 为 null,待 attach 方法调用时设置 mBase;
    public Application() {
        super(null);
    }


    // attachBaseContext 绑定 context,即设置 ContextWrapper 的 mBase 为 ContextImpl
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

一文搞懂 Android Context 上下文,不懂来打我

创建 Application 的 Context,主要分为5步:

  1. 创建 Application 入口,调用 LoadedApk 的 makeApplication 方法;
  2. 创建 ContextImpl 对象,通过 ContextImpl 的静态方法 createAppContext 创建出 ContextImpl 对象;
  3. 创建 Application 对象,通过 Instrucmentation 的 newApplication 方法,内部通过反射创出 Application 对象;
  4. Application 绑定 ContextImpl 对象,通过 Application 的 attach 方法,将 context 设置到 ContextWrapper 的 mBase 变量;
  5. ContextImpl 绑定 Application 对象,通过 ContextImpl 的 setOuterContext 方法,将 Application 设置到 ContextImpl 的 mOuterContext 变量;

启动一个应用时,首先会创建 Application,在创建 Application 的同时,需要创建出对应的 Context 对象,即 ContextImpl,ContextImpl 是 Context 上下文的真正实现类;结合Context 的关系类图,Application 实际是 ContextWrapper 的子类,Application 在 attach 时传入的 ContextImpl 对象被设置给 ContextWrapper 的 mBase;ContextImpl 的 setOuterContext 方法,通过变量 mOuterContext 引用着 Application;最终 Application 和 ContextImpl 存在互相引用的关系。我们可以通过 ContextImpl 找到 对应的组件,也可以通过组件找到对应的 Context。  

2.2、创建 Activity Context

    // ActivityThread.java
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // 1、创建 ContextImpl
        ContextImpl appContext = createBaseContextForActivity(r);
        // 2、创建 Activity
        Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);


        // 3、创建/获取 Application
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);


        // 4、ContextImpl 将外部组件 Activity 绑定到 mOuterContext 变量
        appContext.setOuterContext(activity);
        // 5、将 ContextImpl 绑定到 ContextWrapper(Activity) 的 mBase 变量
        activity.attach(appContext, this, ... app ...);
        return activity;
    }


    // ContextImpl.java
    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        // 创建出 Activity 的 ContextImpl
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
                attributionTag, null, activityInfo.splitName, activityToken, null, 0, classLoader,
                null);
        return context;
    }


    final void setOuterContext(@NonNull Context context) {
        // 将 Activity 绑定到 mOuterContext 变量
        mOuterContext = context;
        ...
    }
    // Instrucmentation.java
    public Activity newActivity(ClassLoader cl, String className, Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        // 创建 Activity
        return getFactory(pkg).instantiateActivity(cl, className, intent);
    }


    // Activity.java
    // attachBaseContext 绑定 context,即设置 ContextWrapper 的 mBase 为 ContextImpl
    final void attach(Context context, ActivityThread aThread, ...) {
        attachBaseContext(context);
    }

一文搞懂 Android Context 上下文,不懂来打我

创建 Activity 的 Context,主要分为5步:

  1. 创建 Activity 的 context 对象,通过 ContextImpl 的静态方法 createActivityContext 创建出 ContextImpl 对象;
  2. 创建 Activity 对象,通过 Instrucmentation 的 newActivity 方法,内部通过 AppComponentFactory 的 instantiateActivity 方法内部通过反射创建出 Activity 对象;
  3. 创建/获取到 Application,Activity 的 attach 方法将 Activity 与 Application 建立绑定关系;
  4. ContextImpl 绑定 Activity 对象,通过 ContextImpl 的 setOuterContext 方法,将 Activity 设置到 ContextImpl 的 mOuterContext 变量;
  5. Activity 绑定 ContextImpl 对象,通过 Activity 的 attach 方法,将 context 设置到 ContextWrapper 的 mBase 变量;

启动一个 Activity 时,首先会获取 Application,然后创建 Activity 的同时,需要创建出对应的 Context 对象,即 ContextImpl,ContextImpl 是 Context 上下文的真正实现类;结合Context 的关系类图,Activity 实际是 ContextWrapper 的子类,Activity 在 attach 时传入的 ContextImpl 对象被设置给 ContextWrapper 的 mBase;ContextImpl 的 setOuterContext 方法,通过变量 mOuterContext 引用着 Activity;最终 Activity 和 ContextImpl 存在互相引用的关系。我们可以通过 ContextImpl 找到对应的组件,也可以通过组件找到对应的 Context。

2.3、Service 的 Context 创建

    // ActivityThread.java
    private void handleCreateService(CreateServiceData data) {
        // 1、创建/获取 Application
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        // 2、创建 Service
        Service service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);
        // 3、创建 ContextImpl
        ContextImpl context = ContextImpl.getImpl(service.createServiceBaseContext(this, packageInfo));


        // 4、ContextImpl 设置 mOuterContext 为 Service
        context.setOuterContext(service);
        // 5、将 ContextImpl 设置给 Context 的装饰器类 ContextWrapper 的 mBase
        service.attach(context, this, data.info.name, data.token, app,ActivityManager.getService());
    }
    // Service.java
    public Context createServiceBaseContext(ActivityThread mainThread, LoadedApk packageInfo) {
        return ContextImpl.createAppContext(mainThread, packageInfo);
    }


    public final void attach(Context context, ActivityThread thread, ...) {
        // 将 ContextImpl 绑定到 ContextWrapper(Service) 的 mBase 变量
        attachBaseContext(context);
        ...
    }


    // ContextImpl.java
    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
            String opPackageName) {
        // 创建 ContextImpl
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
            ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);
        return context;
    }


    final void setOuterContext(@NonNull Context context) {
        // 将 Service 绑定到 mOuterContext 变量
        mOuterContext = context;
        ...
    }

一文搞懂 Android Context 上下文,不懂来打我

创建 Service 的 Context,主要分为5步:

  1. 创建/获取到 Application,Service 的 attach 方法将 Service 与 Application 建立绑定关系;
  2. 创建 Service 对象,AppComponentFactory 的 instantiateService 方法内部通过反射创建出 Service 对象;
  3. 创建 Service 的 context 对象,通过 ContextImpl 的静态方法 createAppContext 创建出 ContextImpl 对象;
  4. ContextImpl 绑定 Service 对象,通过 ContextImpl 的 setOuterContext 方法,将外部组件 Service 绑定到 ContextImpl 的 mOuterContext 变量;
  5. Service 绑定 ContextImpl 对象,通过 Service 的 attach 方法,将 context 绑定到 ContextWrapper(Service) 的 mBase 变量;

启动一个 Service 时,首先会获取 Application,然后创建 Service 的同时,需要创建出对应的 Context 对象,即 ContextImpl,ContextImpl 是 Context 上下文的真正实现类;结合Context 的关系类图,Service 实际是 ContextWrapper 的子类,Service 在 attach 时传入的 ContextImpl 对象被设置给 ContextWrapper 的 mBase;ContextImpl 的 setOuterContext 方法,通过变量 mOuterContext 引用着 Service;最终 Service 和 ContextImpl 存在互相引用的关系。我们可以通过 ContextImpl 找到对应的组件,也可以通过组件找到对应的 Context。

2.4、BroadcastReceiver 的 Context

    // ActivityThread.java
    // 创建 CastReceiver时,如果 Application 未创建,先创建 Application
    private void handleReceiver(ReceiverData data) {
        LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
        ...
        // 1、创建 Application
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        // 2、context 是 Application 绑定的 ContextImpl 类型的 mBase 变量;
        ContextImpl context = (ContextImpl) app.getBaseContext();
        ...
        // 3、创建 BroadcastReceiver
        BroadcastReceiver receiver = packageInfo.getAppFactory().instantiateReceiver(cl, data.info.name, data.intent);
        // 4、创建 ContextWrapper 的子类 ReceiverRestrictedContext
        receiver.onReceive(context.getReceiverRestrictedContext(), data.intent);
    }


    // ContextImpl.java
    // 继承自 ContextWrapper
    class ReceiverRestrictedContext extends ContextWrapper


    // ReceiverRestrictedContext 内的 mBase 是 getOuterContext,即 Application
    final Context getReceiverRestrictedContext() {
        if (mReceiverRestrictedContext != null) {
            return mReceiverRestrictedContext;
        }
        // 5、getOuterContext 返回的是 ContextImpl 绑定的外部组件 Application,将其绑定到 ContextWrapper(ReceiverRestrictedContext) 的 mBase 对象
        return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());
    }


    // ContextWrapper.java
    Context mBase;
    public ContextWrapper(Context base) {
        mBase = base;
    }

一文搞懂 Android Context 上下文,不懂来打我 

动态注册广播时创建 BroadcastReceiver 的 Context,主要分为5步:

  1. 创建/获取到 Application,BroadcastReceiver 使用 Application 作为自己的 Context;
  2. 获取 Application 绑定的 ContextImpl 类型的 mBase 变量;
  3. 创建 BroadcastReceiver,AppComponentFactory 的 instantiateService 方法内部通过反射创建出 BroadcastReceiver 对象;
  4. 创建 BroadcastReceiver 使用 的 context 对象,通过 ContextImpl 的getReceiverRestrictedContext 方法创建出 ContextWrapper(ReceiverRestrictedContext) 对象;
  5. 获取 ContextImpl 绑定的外部组件 Application,将其绑定到 ContextWrapper(ReceiverRestrictedContext) 的 mBase 对象;

动态注册广播时创建 BroadcastReceiver 的 Context,直接使用注册广播的组件作为 context;

// Application 注册广播的 context
BroadCastReceiver onReceive context: com.exaple.test.xxxApplication@14d6f84
BroadCastReceiver onReceive applicationContext: com.exaple.test.xxxApplication@14d6f84
BroadCastReceiver onReceive application: com.exaple.test.xxxApplication@14d6f84


// Activity 注册广播的 context
BroadCastReceiver onReceive context: com.exaple.test.xxxActivity@1e828c0 Intent { act=xxx flg=0x400010 }

ContextImpl 的 getReceiverRestrictedContext 方法返回一个 ContextWrapper,因为 BroadcastReceiver 未继承 ContexWrapper,所以 BroadcastReceiver 使用 context 时,采用方法入参的形式,在 onReceiver 方法传入一个 ContextWrapper 对象,即 ContextWrapper(ReceiverRestrictedContext) ,保持与 Activity/Service 内使用 Context 结构的统一性(装饰器模型);在 getReceiverRestrictedContext 方法内会创建/获取 ReceiverRestrictedContext 对象,ReceiverRestrictedContext 继承自 ContextWrapper,构造方法传入的 getOuterContext 参数被赋值给 mBase,getOuterContext 方法返回的是ContextImpl 绑定的外部组件 Application 对象,所以在 BroadcastReceiver 内 context 实际上是 Application 的 context。

2.5、ContentProvider 的 Context 创建

    // ActivityThread.java
    // 参数中的 context 是 ContextImpl(Application/Activity/Service/ContentProvider均有可能) 对象,可能是同一进程或其他进程传递的,
    private ContentProviderHolder installProvider(Context context ...) {
         Context c = null;
         ApplicationInfo ai = info.applicationInfo;
         // 1、同一进程启动 Provider,直接使用 ContextImpl context
         if (context.getPackageName().equals(ai.packageName)) {
             c = context;
         } else if (mInitialApplication != null &&
                 mInitialApplication.getPackageName().equals(ai.packageName)) {
             c = mInitialApplication;
         } else {
            // 2、其他进程启动 Provider,context 是其他进程的 ContextImpl,需要创建 ContextImpl
             try {
                 c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE);
             } catch (PackageManager.NameNotFoundException e) {
             }
         }
         // 3、创建 Provdier
         ContentProvider localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);
         // 4、将 ContextImpl 绑定到 ContentProvider 的 mContext 变量
         localProvider.attachInfo(c, info);
    }


    // ContextImpl.java
    public Context createPackageContext(String packageName, int flags) {
        return createPackageContextAsUser(packageName, flags, mUser);
    }


    @Override
    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) {
        ...


        ContextImpl c = new ContextImpl(this, mMainThread, ...);
        ...
    }

日志可以证明,ContentProvider 使用的 context 是 Application

// ContentProvider 内 Context 和 Application Context
ContentProvider onCreate context: com.exaple.test.xxxApplication@14d6f84
ContentProvider onCreate applicationContext: com.exaple.test.xxxApplication@14d6f84
ContentProvider onCreate application: com.exaple.test.xxxApplication@14d6f84

一文搞懂 Android Context 上下文,不懂来打我

创建 ContentProvider 的 Context,主要分为4步:

  1. 启动 ContenProvider,判断启动进程与 ContentProvider 包名是否相同,相同直接使用传入的 ContextImpl对象,否则需要创建 ContextImpl;
  2. 创建 ContextImpl 对象,通过 ContextImpl 的 createPackageContext 方法创建新的 ContextImpl 对象;
  3. 创建 ContentProvider 对象,AppComponentFactory 的 instantiateProvider 方法内部通过反射创建出 ContentProvider 对象;
  4. ContentProvider 绑定 ContextImpl 对象,通过 ContentProvider 的 attachInfo 方法,将 context 设置到 ContentProvider 的 mContext 变量;

启动一个 ContentProvider 时,首先会判断调用进程与 ContentProvider 是否为同一进程,决定是否需要创建出对应的 Context 对象,即 ContextImpl,ContextImpl 是 Context 上下文的真正实现类;ContentProvider 不是 Context 的子类,而是采用组合方式,持有 Context 对象,ContentProvider 在 attachInfo 时传入的 ContextImpl 对象被设置给 mContext。

转载自:https://juejin.cn/post/7382892803313188875
评论
请登录