Context可以說是Android開發(fā)中非常高頻使用的內容了。彈出Dialog、Toast;打開新的Activity,包括獲取項目內resource資源都需要Context對象。那么Context到底是個啥,為啥Android中這么多的操作都需要Context來完成。
Context是個啥
Context本身首先是一個抽象類,我們對它常用的稱呼是上下文,官方的注釋是這么說的
/**
* Interface to global information about an application environment. This is
* an abstract class whose implementation is provided by
* the Android system. It
* allows access to application-specific resources and classes, as well as
* up-calls for application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/
大概的意思就是Context是一個與應用環(huán)境相關的全局信息接口,由Android系統(tǒng)提供實現的抽象類。那么Context內部到底都做了什么才讓自己變成一個“海王”,讓我們的項目里處處都有它的身影呢?
首先總覽一下Context的內容構成:
- 自定義注解 => 主要用來表達{模式}、{標識}、{類型}之類的概念
- 靜態(tài)常量 => 主要配合自定義注解使用,用來定義注解內的參數范圍
- 抽象方法 => 占Context的主要部分,Context內定義了大量的抽象方法,向外部提供了非常多的功能調用,例如我們比較熟悉的getString(resId)、startActivity(intent)、startService(intent) 等等,內部各類抽象方法提供了非常豐富的能力,這也就是Context在項目中隨處可見的一部分原因。
- 實現方法 => 數量較少,但也存在,提供一些基礎能力。getMainExecutor()的實現與子類重寫
從這些構成來看,Context并不像是個上下文,不能說毫不相關只能說是莫名其妙。

哪里都看不出這是個什么承上啟下的內容,甚至它是個抽象類,也沒有繼承實現任何類;它所承擔的能力更多是聲明功能和承載信息,提供給自己的實現類;Context更像是一個信息工具集合體,作為一個Application廣泛認同的內容承載信息,聲明功能范圍。
Context的實現類
有些朋友可能發(fā)現了,上面在舉例抽象方法構成時提到的一些方法大家都用過,但是使用的時候有時候要用context調用有時候直接調用就行了,莫非Context是個什么全局基類?[?_??]
實際上這個和我們的使用場景有關,Context直接的實現類有兩個:ContextImpl、ContextWrapper,而這兩個類中實際干活的類只有ContextImpl,ContextWrapper是ContextImpl的裝飾類,在它內部有一個mBase的字段,實際就是指向ContextImpl實例的對象。
//ContextWrapper源碼節(jié)選
Context mBase;
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
Context的實現關系并沒有到此為止,接下來登場的就是大家熟悉的老朋友了,有請馮鞏老師(不是, ContextWrapper后續(xù)的繼承類分別是Application、Service、ContextThemeWrapper,而ContextThemeWrapper則專門封裝了getTheme的內容,不用多想,ContextThemeWrapper的子類就是Activity,至此,Context整個家族都到齊了。

梳理一遍Context的設計,Context作為基類聲明了抽象方法,由ContextImpl實現其中的方法,接著ContextWrapper作為裝飾類引用ContextImpl的實例,而Service、Application、Activity都直接或間接的集成ContextWrapper,并在此基礎上進行了一些拓展。
好了,到這似乎都清楚了。

了嗎?
雖然理清了整個繼承關系,但是在上面的代碼里展示的很明顯,startActivity里實際執(zhí)行的是mBase對象的方法,也就是ContextImpl的對象,那么mBase得到賦值的時機也就成為了ContextImpl生效的關鍵。
Context是如何起作用的
Context生效是通過層層實現來達到目的的,也就是說使用者將ContextImpl實例賦值給自己的mBase后,就可以隨意使用Context的實現了。
接下來就是追根溯源時間
Application Context準備過程
ApplicationContext需要在Application應用啟動后就能夠使用,至少我們在Application的onCreate中就已經在使用Context了,那么就需要從應用啟動開始排查Context相關的內容。
前面太多的源碼加上Android10里LifeCycle糾纏太深,我還沒有研究明白;就從最貼近創(chuàng)建的部分來看吧
// ActivityThread代碼節(jié)選
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//...省略內容...
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
}
//...省略內容...
return activity;
}
這就是在啟動桌面圖標,啟動首頁Activity過程中創(chuàng)建Application的動作,r.packageInfo指向的是一個LoadedApk對象,通過它內部定義的方法,使用mInstrumentation就可以創(chuàng)建出我們的Application對象。
等等,朋友,你這車輪都掛樹上去了。
這個LoadedApk和我們的Context有啥關系?你信不信我

別急別急,創(chuàng)建Application繞不過它,我們先看看創(chuàng)建Application的過程。
// LoadedApk源碼節(jié)選
public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {
// 這里可以看到LoadedApk里維護了一個application對象,避免重復創(chuàng)建
if (mApplication != null) {
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
Application app = null;
//...省略部分...
try {
//...省略部分...
//創(chuàng)建ContextImpl實例,之后使用Instrumentation創(chuàng)建Application,傳入context對象
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
appContext.setOuterContext(app);
}
//...省略部分...
// 剛剛創(chuàng)建的Application直接保存起來了
mApplication = app;
return app;
}
上面的過程可以看到,Context會調用createAppContext來創(chuàng)建Application使用的Context,其中參數就包含了LoadedApk對象本身。我們再來看看createAppContext做了什么工作。
// ContextImpl源碼節(jié)選
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
// 傳入的LoadedApk在ContextImpl構造函數中會賦值給全局變量{mPackageInfo},后續(xù)部分獲取Application相關內容的方法會通過它來完成
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,null);
context.setResources(packageInfo.getResources());
return context;
}
獲取到傳入的LoadedApk對象后,直接實例化ContextImpl對象,并且會賦值給全局變量{mPackageInfo};在ContextImpl中也能看到部分方法對mPackageInfo的引用,感興趣的朋友可以翻翻ContextImpl的源碼,這里就不展開了。上面的內容主要是看看Context和LoadedApk之間的聯系。
接下來是Application的創(chuàng)建設置ContextImpl實例
// Instrumentation源碼節(jié)選
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
// AppComponentFactory最終指向的是AppComponentFactory,內部的實現是
// return (Application) cl.loadClass(className).newInstance();
Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);
app.attach(context);
return app;
}
好家伙,終于是找到了,在Application創(chuàng)建完成以后,直接用Application對象調用attach(ContextImpl實例),通過attach方法把ContextImpl傳入了Application中,最后來看看Application的attach實現吧。
// Application源碼節(jié)選
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
// ContextWrapper源碼節(jié)選
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
這個過程需要回憶一下上面提到的,Application是繼承于ContextWrapper的,在調用attachBaseContext時傳入的是ContextImpl的實例,通過這個方法完成了裝飾類使用實例的設置。至此Application創(chuàng)建及其Context創(chuàng)建、配置的過程就走完一遍了,整過過程還算清晰,粗略的可以分成3步,就和大象進冰箱一樣
- Application實例創(chuàng)建
- ContextImpl實例創(chuàng)建
- Application設置ContextImpl實例
在此之后,Application中就可以隨意調用Context抽象類中定義的方法了,所有的活ContextImpl都會認真負責的搞定;但在Application的attach之前,ContextImpl是沒有到崗的,因此attach之后才是調用Context的正確時機。
Activity Context準備過程
看過了Application創(chuàng)建的過程,我們再來看看Activity的Context是如何創(chuàng)建的,之前講到Application創(chuàng)建時大家就應該發(fā)現了,創(chuàng)建Application的方法名反而是performLaunchActivity,我們再來細致的看一看這個方法。
// ActivityThread源碼節(jié)選
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
// ...省略部分...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(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,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
// ...省略部分...
}
// ...省略部分...
return activity;
}
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
// ...省略部分..
ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo,
r.activityInfo, r.token, displayId, r.overrideConfig);
// ...省略部分..
return appContext;
}
又是熟悉的方法,流程也和Application基本一致,可以看到Activity使用的Context是由ContextImpl調用的createActivityContext創(chuàng)建的;到這我們可以看出來,ContextImpl對和Activity提供了特有的初始化方法,在創(chuàng)建方法內部還單獨配置了resource對象,感興趣的朋友可以深入了解一下。
值得注意的是,AppContext實例化以后,通過setOuterContext將Activity對象賦值給了自己的mOuterContext對象,在Activity的attach之后,Activity和ContextImpl將互相持有對方的引用,Activity父類中的mBase和ContextImpl中的mOuterContext。
最后不出意外,activity的Context也是在attach中賦值的。
// Activity源碼節(jié)選
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
// ...省略部分...
}
在attachBaseContext調用之后,Activity中的Context也就獲取到了ContextImpl實例,之后就能夠隨意調用Context方法了。
Service Context準備過程
相對另外兩個Context實現類來說,Service Context準備的過程可以說是非常的直接了
// Activity源碼節(jié)選
private void handleCreateService(CreateServiceData data) {
// ...省略部分...
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
// ...省略部分...
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException("Unable to create service " + data.info.name + ": " + e.toString(), e);
}
}
}
整個過程的主要部分沒有什么特殊的,同樣是在attach之后完成的ContextImpl賦值。
至此Context和它的一大家子我們都有了一定的了解了,關于Context具體做能做什么就需要我們更深入的去看Context的內容,要知道它是怎樣實現的我們可以到ContextImpl中找到答案;如果和它的使用場景有關,就需要區(qū)分Application、Activity、Service來獨立的分析。
完整的了解一次Context過程,收獲頗豐,在梳理各個環(huán)節(jié)時,因為是初次接觸,會遇到各個讓人困惑的節(jié)點,有的和設計思路有關,有的和應用開發(fā)中的問題有關。雖然沒有吧所有問題都完全解決,畢竟源碼可以說是海量,但對之前熟練用的工具有了新的認識。不能說完全沒用,只能說毫無意義(╯°□°)╯︵ ┻━┻ 開個玩笑,提升之路任重道遠,下篇總結見。
