一文搞懂 Android Context 上下文,不懂来打我
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 上下文能力),符合开闭原则;
我们在开发过程中,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;
}
创建 Application 的 Context,主要分为5步:
- 创建 Application 入口,调用 LoadedApk 的 makeApplication 方法;
- 创建 ContextImpl 对象,通过 ContextImpl 的静态方法 createAppContext 创建出 ContextImpl 对象;
- 创建 Application 对象,通过 Instrucmentation 的 newApplication 方法,内部通过反射创出 Application 对象;
- Application 绑定 ContextImpl 对象,通过 Application 的 attach 方法,将 context 设置到 ContextWrapper 的 mBase 变量;
- 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);
}
创建 Activity 的 Context,主要分为5步:
- 创建 Activity 的 context 对象,通过 ContextImpl 的静态方法 createActivityContext 创建出 ContextImpl 对象;
- 创建 Activity 对象,通过 Instrucmentation 的 newActivity 方法,内部通过 AppComponentFactory 的 instantiateActivity 方法内部通过反射创建出 Activity 对象;
- 创建/获取到 Application,Activity 的 attach 方法将 Activity 与 Application 建立绑定关系;
- ContextImpl 绑定 Activity 对象,通过 ContextImpl 的 setOuterContext 方法,将 Activity 设置到 ContextImpl 的 mOuterContext 变量;
- 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;
...
}
创建 Service 的 Context,主要分为5步:
- 创建/获取到 Application,Service 的 attach 方法将 Service 与 Application 建立绑定关系;
- 创建 Service 对象,AppComponentFactory 的 instantiateService 方法内部通过反射创建出 Service 对象;
- 创建 Service 的 context 对象,通过 ContextImpl 的静态方法 createAppContext 创建出 ContextImpl 对象;
- ContextImpl 绑定 Service 对象,通过 ContextImpl 的 setOuterContext 方法,将外部组件 Service 绑定到 ContextImpl 的 mOuterContext 变量;
- 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;
}
动态注册广播时创建 BroadcastReceiver 的 Context,主要分为5步:
- 创建/获取到 Application,BroadcastReceiver 使用 Application 作为自己的 Context;
- 获取 Application 绑定的 ContextImpl 类型的 mBase 变量;
- 创建 BroadcastReceiver,AppComponentFactory 的 instantiateService 方法内部通过反射创建出 BroadcastReceiver 对象;
- 创建 BroadcastReceiver 使用 的 context 对象,通过 ContextImpl 的getReceiverRestrictedContext 方法创建出 ContextWrapper(ReceiverRestrictedContext) 对象;
- 获取 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
创建 ContentProvider 的 Context,主要分为4步:
- 启动 ContenProvider,判断启动进程与 ContentProvider 包名是否相同,相同直接使用传入的 ContextImpl对象,否则需要创建 ContextImpl;
- 创建 ContextImpl 对象,通过 ContextImpl 的 createPackageContext 方法创建新的 ContextImpl 对象;
- 创建 ContentProvider 对象,AppComponentFactory 的 instantiateProvider 方法内部通过反射创建出 ContentProvider 对象;
- 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