前言
-
“一個進程有多少 Context 對象基茵?” 這是一個比較初級的問題奋构。但是,從這個問題卻可以看出面試者對
Android
源碼是否具備最基本的認識拱层; - 如果你試圖直接從網(wǎng)上尋找答案弥臼,而不是自己閱讀源碼,很可能你會給出這樣的答案:
舱呻, 因為網(wǎng)上 99% 的文章 / 面經(jīng)就是這么講的醋火。但是,你覺得他們說得對嗎箱吕?Context
對象個數(shù) =Service
對象個數(shù) +Activity
對象個數(shù) + 1
目錄
1. Context 繼承關(guān)系
Context
是一個抽象類芥驳,具體的實現(xiàn)類有Application
、Activity
茬高、Service
與ContextImpl
兆旬。為方便區(qū)分,通常也稱為ApplicationConext
怎栽、ActivityContext
與ServiceContext
丽猬,具體 UML 類圖如下:
可以看到宿饱,除了我們熟悉的Application
、Activity
脚祟、Service
谬以,繼承關(guān)系上還有ContextWrapper
與ContextThemeWrapper
,它們的作用 & 職責如下:
-
ContextWrapper
定義:
Context
包裝類作用:持有基礎(chǔ)對象的引用
(mBase)
由桌,并且實現(xiàn)了Context
接口为黎,將所有方法調(diào)用請求轉(zhuǎn)發(fā)給基礎(chǔ)對象
// ContextWrapper.java
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
// 【分析點1:綁定基礎(chǔ)對象(見todo)】
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
@Override
public void startActivity(Intent intent) {
// 轉(zhuǎn)發(fā)給 mBase
mBase.startActivity(intent);
}
-
ContextThemeWrapper
- 定義:
Context
包裝類
【todo】
- 定義:
2. Application 對象
我們都知道,在啟動四大組件(Activity行您、Service铭乾、ContentProvider, BroadcastReceiver)
時,如果對應(yīng)的進程未啟動娃循,就需要先創(chuàng)建進程炕檩,相應(yīng)地也會創(chuàng)建一個Application
對象。若還不了解捌斧,請務(wù)必閱讀:《Android | 帶你理解 Application 的創(chuàng)建過程》。簡單來說:
- 在
system_server
進程骤星,通過AMS#getProcessRecordLocked(...)
獲取進程信息(ProcessRecord)
经瓷; - 若不存在,則調(diào)用
AMS#startProcessLocked(...)
創(chuàng)建進程 - 在
Zygote
孵化目標進程之后洞难,在目標進程反射執(zhí)行ActivityThread#main()
舆吮,并最終在ActivityThread#handleBindApplication(...)
中創(chuàng)建Application
對象
// ActivityThread.java
Application mInitialApplication;
final ArrayList<Application> mAllApplications = new ArrayList<Application>();
private void handleBindApplication(AppBindData data) {
// ...
Application app;
// data.info 為 LoadedApk.java
app = data.info.makeApplication(data.restrictedBackupMode, null);
// ...
mInitialApplication = app;
// ...
}
// LoadedApk.java
private Application mApplication;
public Application makeApplication(...) {
// 創(chuàng)建基礎(chǔ)對象 ContextImpl
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
// 反射調(diào)用創(chuàng)建 Application 對象
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
// ContextImpl 也持有包裝類 Application
appContext.setOuterContext(app);
// 保存創(chuàng)建的 Application 對象
mActivityThread.mAllApplications.add(app);
mApplication = app;
}
// Instrumentation.java
public Application newApplication(ClassLoader cl, String className, Context context) {
// 反射調(diào)用創(chuàng)建 Application 對象
Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);
app.attach(context);
return app;
}
// Application.java
final void attach(Context context) {
// 設(shè)置包裝類 Application 的基礎(chǔ)對象
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
總結(jié)要點如下:
- 一個
Application
對象相當于存在兩個Context
對象(代理對象與基礎(chǔ)對象) -
Application
對象與ContextImpl
對象相互引用
3. Activity 對象
這一節(jié)我們來看Activity
對象的創(chuàng)建過程,若還不了解队贱,請務(wù)必閱讀:《Android | 帶你理解 startActivity() 的執(zhí)行過程》色冀,簡單來說:
- 創(chuàng)建
Application
對象之后,最后在ActivityThread#handleLaunchActivity(...)
中創(chuàng)建Activity
對象
// ActivityThread.java
public Activity handleLaunchActivity(...) {
// ...
final Activity a = performLaunchActivity(r, customIntent);
// ...
}
private Activity performLaunchActivity(...) {
// ...
// 創(chuàng)建基礎(chǔ)對象 ContextImpl
ContextImpl appContext = ContextImpl.createActivityContext(...);
// 反射調(diào)用創(chuàng)建 Activity 對象
Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
// 相互引用
appContext.setOuterContext(activity);
activity.attach(appContext,...)
// ...
}
總結(jié)要點如下:
- 一個
Activity
對象相當于存在兩個Context
對象(代理對象與基礎(chǔ)對象) -
Activity
對象與ContextImpl
對象相互引用
4. Service 對象
這一節(jié)我們來看Service
對象的創(chuàng)建過程柱嫌,若還不了解锋恬,請務(wù)必閱讀:《Android | 帶你理解 startService() 的執(zhí)行過程》與《Android | 帶你理解 bindService() 的執(zhí)行過程》,簡單來說:
創(chuàng)建
Application
對象之后编丘,startService(...)
最后在ActivityThread#handleCreateService(...)
中創(chuàng)建Service
對象創(chuàng)建
Application
對象之后与学,bindService(...)
最后在ActivityThread#handleCreateService(...)
中創(chuàng)建Service
對象,在ActivityThread#handleBindService(...)
中綁定Service
(注意:兩個方法都在遠程進程執(zhí)行)
// ActivityThread.java
private void handleCreateService(...) {
// ...
// 反射調(diào)用創(chuàng)建 Serivce 對象
Serivce service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);
// 創(chuàng)建基礎(chǔ)對象 ContextImpl
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
// 相互引用
context.setOuterContext(service);
service.attach(...);
}
總結(jié)要點如下:
- 一個
Serivce
對象相當于存在兩個Context
對象(代理對象與基礎(chǔ)對象) -
Serivce
對象與ContextImpl
對象相互引用
5. 問題回歸
到這里嘉抓,我們回歸開頭提出的問題索守,結(jié)論是:Context個數(shù) = Service個數(shù) + Activity個數(shù) + Application個數(shù) + ContextImpl個數(shù)。
考慮到Application
等與ContextImpl
間的代理關(guān)系抑片,也可以寫為:Context 個數(shù) = 2 x(Service 個數(shù) + Activity 個數(shù) + Application 個數(shù)) + 其他 ContextImpl 個數(shù)
可能有的小伙伴會問卵佛,“這個問題沒有實際價值啊,不知道答案照樣可以正常開發(fā)”。是的截汪,如果僅僅滿足于對Context
的字典式認知疾牲,那么這個問題確實不會發(fā)揮太大的價值。
更重要的是以這個問題為線索衙解,去理解四大組件的啟動流程 & 原理阳柔,甚至去發(fā)掘更多問題,例如:
5.1 既然 Android 的另外兩大組件 ContentProvider & BroadcastReceiver 不是 Context 的實現(xiàn)類蚓峦,那么它們是怎么拿到 Context 對象的引用呢盔沫?
請務(wù)必閱讀:《Android | 帶你理解 Broadcast 廣播機制》 & 《Android | 帶你理解 ContentProvider 機制》
5.2 ApplicationContext、ActivityContext 與 ServiceContext有什么區(qū)別枫匾?
請務(wù)必閱讀:《Android | 徹底拆解 Context 的功能模塊》
5.3 View & Fragment & Window 的getContext()是ActivityContext嗎?
這里有的小伙伴可能就會說“當然是啦”拟淮,真的是這樣嗎干茉?如果這個View
是一個懸浮窗呢?請務(wù)必閱讀文章:
《Android | View & Fragment & Window 的getContext() 真的是Activity嗎很泊?》
5.4 第三方庫如何獲得Context對象角虫?
請務(wù)必閱讀:《Android | 使用 ContentProvider 無侵入獲取 Context》
請繼續(xù)關(guān)注彭旭銳的簡書!
參考資料
《理解Android Context》 —— Gityuan 著
推薦閱讀
- Java | 帶你理解 ServiceLoader 的原理與設(shè)計思想
- Java | Object obj = new Object()占用多少字節(jié)委造?
- Java | 聊一聊編譯過程(編譯前端 & 編譯后端)
- Android | 使用 ContentProvider 無侵入獲取 Context
- Android | 帶你理解 NativeAllocationRegistry 的原理與設(shè)計思想
- Android | 談一談 Matrix 與坐標變換
- Android | 一文帶你全面了解 AspectJ 框架
- Android | 這是一份詳細的 EventBus 使用教程
- 計算機組成原理 | 為什么浮點數(shù)運算不精確戳鹅?(阿里筆試)