是什麼
從應用場景的角度來說,他是一個場景,一個使用者與系統互動的過程.比如當你看簡訊時,場景包括簡訊的頁面,以及隱藏在後面的資料
提到頁面,我們應該能夠聯想到 Activity
沒錯,Activity,Service都是一個Context
從JAVA語言角度來說,Context是一個抽象類,抽象類中包含了Application環境的一些函式,設計角度而言,Context僅提供某些功能, extends 才是類的本質,即 Activity 的本質是一個 Context ,其所實現的其他介面只是為了擴充 Context 的功能而已,擴充後的類稱之為 Activity 或 Service
有多少Context
- Application一個Context
- 多少個Activity就有多少個Context
- 多少個Service就有多少個Context
Context個數=1 + Activity個數 + Service個數
Application的Context建立
在部落格 ActivityThread.main過程 中分析中可以知道, handleBindApplication 函式中會呼叫 makeApplication
makeApplication會建立Application以及建立 ContextImpl
建立 ContextImpl
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
複製程式碼
這裡的this是 LoadedApk 物件,該物件是在 handleBinderApplication 中賦值
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
複製程式碼
在該函式中,會根據 AppBindData(handleBinderApplication中的引數) 中的ApplicationInfo的mPackageName建立一個PackageInfo物件並儲存為ActivityThread類的全域性物件
顯然,一個應用程式中所有Activity或者Application或Servie,他們的mPackageName是一樣的,即為包名,因此ActivityThread只會有一個全域性的PackageInfo物件
在 newApplication的函式中會呼叫 Application 的 attach
attach
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
複製程式碼
檢視 attachBaseContext
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
複製程式碼
這個mBase就是 ContextWrapper 中的 Context
Activity的Context建立
在 Launcher啟動流程 的分析中可以知道,handleLaunchActivity 會呼叫到 performLaunchActivity,該函式會呼叫 createBaseContextForActivity
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
final int displayId;
...
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
...
return appContext;
}
複製程式碼
createActivityContext
createActivityContext 中的 packageInfo 資訊和上小節分析的流程基本一致,他也是全域性的
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
...
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
...
return context;
}
複製程式碼
attach
建立Context完成後,呼叫 activity 的 attach 函式
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);
複製程式碼
attach 函式中做了很多的賦值操作,其中 attachBaseContext 的函式和Application的 attachBaseContext 中作用一致,把context賦值給 ContextWrapper 的 mBase
:::danger 筆記 因此,當我們翻閱Activity原始碼,看到mBase時,就應該去找 ContextImpl 裡的方法 :::
Service的Context建立
Service的啟動和Activity類似,最終同樣會呼叫到ActivityThread裡的函式,為 scheduleCreateService,接著呼叫 handleCreateService
在 handleCreateService 中會建立Context
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();
複製程式碼
Context的建立方式和Application一致,同樣在建立後會呼叫 attach 進行一些賦值操作,同樣也有之前分析的 mBase
總結
不同Context子類中PackageInfo物件來源
類名 | 遠端資料類 | 本地資料類 | 賦值方式 |
---|---|---|---|
Application | ApplicationInfo | AppBindData | getPackageInfoNoCheck |
Activity | ActivityInfo | ActivityClientRecord | getPackageInfo |
Service | ServiceInfo | CreateServiceData | getPackageInfoNoCheck |
參考書籍: Android核心剖析