Android开发经常会用到Context,例如:context.getResource(),context.startActivity(),context.startService()等等。那么Context到底是什么呢,为什么我们的Activity中、Service中、BroadcastReceiver中还有Application中都有他的身影。这个被我们整天叫做“上下文”的对象到底在App中起到了什么作用呢?这些就是我们要分析理解的重点。
Context 的定义
Context 是一个关于应用环境的抽象类
,它的实现由android系统提供。用来访问一些应用内资源和类,也可以调用系统服务开启Activity,Service或发送广播等。
Context 继承关系
Context本身是一个 抽象类,其直接继承者
有ContextImpl
和 ContextWrapper
两大类。图中表示了组件与Context之间的继承关系。
从名字可以看出,ContextWrapper 是一个Context的包装类,而ContextImpl似乎是真正实现了Context核心功能的实现类。 带着这个思路我们查看一下ContextImpl的源码:
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*
*/
class ContextImpl extends Context {
。。。此处省略2000多行代码。。。
}
其实在这个问题上,我们需要关注的只有类注释就够了,翻译过来是:这是通用的Context 方法的实现类,为Activity等组件提供base context的实例。再来对比看下ContextWrapper的源码:
/**
* Proxying implementation of Context that simply delegates all of its calls to
* another Context. Can be subclassed to modify behavior without changing
* the original Context.
*
*/
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
。。。此处省略1000多行代码。。。
}
同样提供了类注释:这是一个Context的代理类
,它代理了所有 Context抽象类 的API。该代理类能够被再继承,以改变Context 的行为,同时又不去改变被代理Context 的原有行为。
我们深入代码(代码这里就省略了,全都是调用的super)则会发现,确实ContextWrapper除了上面截取出来的一段代码外,其余方法全部都是调用了super。这里坐实了它的代理模式,而它构造方法中传入的mBase,也正是ContextImpl实例。我们可以拿Activity举例来看mBase的赋值时机。
在Activity的attach()方法中调用了ContextWrapper的attachBaseContext()
,并传入了一个Context的实例。分析过Activity启动过程之后,我们会知道Activity.attach()会在ActivityThread.performLaunchActivity()的时候调用到。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
。。。省略。。。
ContextImpl appContext = createBaseContextForActivity(r);//创建Activity使用的ContextImpl实例
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity( //顺便能够发现,activity的实例化是谁干的。
cl, component.getClassName(), r.intent);
。。。省略。。。
} catch (Exception e) {
。。。省略。。。
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
。。。省略。。。
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,//为新的activity实例进行初始化attach
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
}
}
。。。省略。。。
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
。。。省略。。。
我们省略了大段其他逻辑来凸显出activity的实例化过程和attach的调用过程,发现performLaunchActivity()
做了四件事:
- 为Activity生成ContextImpl实例,叫做appContext。
- 从appContext中获取ClassLoader,实例化Activity。
- 获取Application对象。
- 调用activity.attach(),把appContext,application对象统统关联进去。
- 调用activity.onCreate(),执行我们熟知的生命周期。
传入的appContext来自于createBaseContextForActivity()
。继续跟进,能够在ContextImpl类中找到createActivityContext()
:
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
String[] splitDirs = packageInfo.getSplitResDirs();
ClassLoader classLoader = packageInfo.getClassLoader();
if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
try {
classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
} catch (NameNotFoundException e) {
// Nothing above us can handle a NameNotFoundException, better crash.
throw new RuntimeException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
// ContextImpl 的构造函数是private,因此只能使用ContextImpl提供 的静态方法进行初始化获取。
// ContextImpl实例持有mainThread、packageInfo、classLoader,如果是给Activity用,则还会有非空的activityToken。(某些Window、Dialog为何只能使用Activity或Activity的Context去加载的原因)
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
? packageInfo.getCompatibilityInfo()
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
// Create the base resources for which all configuration contexts for this Activity
// will be rebased upon.
context.setResources(resourcesManager.createBaseActivityResources(activityToken,
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader));
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
context.getResources());
return context;
}
最终我们弄清楚了,Android组件中的context和组件之间的关系,其实就是在使用不同的组件过程中(比如Activity),组件通过静态代理的方式调用到ContextImpl的核心功能,同时针对自身的特性(比如Activity需要显示界面,定制主题等)进行一些行为变更。换句话说,与其将Activity叫做Context,不如说Activity代理了Context。Service也类似。
有时候面试官可能会问这么一个问题:一个App中有多少个Context? 根据图片中的继承关系可以知道,Activity、Service、Application都是Context的子类。因此宏观上来看,有多少Activity+Service+Application,就会有多少个Context。要注意的是,Application并不是每个App只有一个,而是App开了几个进程,就会有几个Application实例。这个答案精确的来说其实并不准确,因为我们发现,Context的另一个子类:ContextImpl并没有被包含进去。而组件中所持有的mBase,就正好是ContextImpl的实例。mBase事关启动流程,这里就不再展开叙述。
思考:
-
Activity中,this作为 Context 和 getBaseContext()获取的Context有什么区别?
this 是Activity对象本身,虽然也是Context的子类,但它的直接父类是ContextThemeWrapper,而不是Context。对照上图继承关系会发现,ContextThemeWrapper继承于ContextWrapper,源码中继承了绝大部分父类的行为,并增加了显示相关的API,比如setTheme()设置主题。而getBaseContext获取到的则是mBase变量在Activity实例化时传入的context实例。
-
Activity中能够获取到Application实例,也能够获取到 ContextImpl实例,那么他们创建的时机是什么?
Activity中的getApplication()和getBaseContext()方法所获取的对象,均是在Activity创建时被关联进来的。ActivityThread在执行performLaunchActivity()时,获取了application和context,在调用Activity.attach()的时候设置进去。
-
Service中的ContextImpl实例创建时机是什么? 类似于Activity,Service中也存在着attach()的方法,它的调用时机是
ActivityThread.handleCreateService()
。这其中干了5件事儿:- 获取ClassLoader,实例化Service。
- 通过ContextImpl.createAppContext()静态方法,实例化ContextImpl。
- 通过packageInfo.makeApplication()获取Application实例。
- 调用service.attach()关联上述context和application实例。
- 调用service.onCreate()。