android 中application context 和 activity Context 闡釋
前言
Context在開發Android應用的過程中扮演著非常重要的角色,
例如,啟動一個 Activity需要使用context.startActivity方法,
將一個xml檔案轉換為一個View物件也需要使用Context物件,
彈窗需要context,資源獲取需要,可以說沒有Context,android開發
無從談起
Applicaiton Context 和Activity Context 是否是同一個,能否混淆使用?
帶著這個問題接著看;
類結構圖
package android.content;
package android.app;
注意:
因為ContextImpl 和Context 不在同一個包下,用uml生成工具沒能生成出來。下面的圖是手動畫的,應該合起來看。
Context本身是一個純的abstract類。
ContextWrapper是對Context的一個包裝而已,它的內部包含了一個 Context物件,其實對ContextWrapper的方法呼叫最終都是呼叫其中的Context物件完成的,
ContextThremeWrapper,很明顯和Theme有關,所以Activity從ContextThemmWrapper繼承,而 Service,application 沒有主題 所從ContextWrapper繼承,
ContextImpl是唯一 一個真正實現了Context中方法的類。
從上面的繼承關係來看,每一個Activity就是一個Context,每一個Service就是一個Context,每一個Application就是一個Context,這也就是為什麼使用Context的地方可以被Activity,Service或者Application替換了。
根據上面所介紹的,實現Context的只有Contextimpl類,其它是對context的包裝,最終呼叫的還是的Contextimpl類,所以Activity,application,service 建立的時候肯定要建立一個ContextImpl物件,賦值給 Activity,application,service 中的context。口說無憑,跟著原始碼來看。
步驟一
分析startActivity()流程的,最後進入T和threadActivity中的 performLaunchActivity()方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
.....
.....
}
}
進入 createBaseContextForActivity()方法
private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
appContext.setOuterContext(activity);
Context baseContext = appContext;
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
try {
IActivityContainer container =
ActivityManagerNative.getDefault().getEnclosingActivityContainer(r.token);
final int displayId =
container == null ? Display.DEFAULT_DISPLAY : container.getDisplayId();
if (displayId > Display.DEFAULT_DISPLAY) {
Display display = dm.getRealDisplay(displayId, r.token);
baseContext = appContext.createDisplayContext(display);
}
} catch (RemoteException e) {
}
// For debugging purposes, if the activity's package name contains the value of
// the "debug.use-second-display" system property as a substring, then show
// its content on a secondary display if there is one.
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int displayId : dm.getDisplayIds()) {
if (displayId != Display.DEFAULT_DISPLAY) {
Display display = dm.getRealDisplay(displayId, r.token);
baseContext = appContext.createDisplayContext(display);
break;
}
}
}
return baseContext;
}
這裡這是證明 activity 建立過程中會初始化 contextImpl 實現類,關於Application,service 自行檢視原始碼,這裡不過多解釋。
建議直接檢視 contextImpl 類,該類裡面羅列了所有初始化方法。
到這裡共說明了
1.application,service,activity與Context的關係。
2.application 和 activity之間是否可以替換,為什麼不可以替換
3.context的類繼承樹
在此總結一下:
(1)Context是一個抽象類,ContextWrapper是對Context的封裝,它包含一個Context型別的變 量,ContextWrapper的功能函式內部其實都是呼叫裡面的Context型別變數完成的。 Application,Service,Activity等都是直接或者間接繼承自ContextWrapper,但是並沒有真正的實現其中的功 能,Application,Service,Activity中關於Context的功能都是通過其內部的Context型別變數完成的,而這個變數的 真實物件必定是ContextImpl,所以沒建立一個Application,Activity,Servcice便會建立一個 ContextImpl,並且這些ContextImpl中的mPackages和mResources變數都是一樣的,所以不管使用Acitivty還 是Service呼叫getResources得到相同的結果
(2)在一個apk中,Context的數量等於Activity個數+Service個數+application個數.
關於上面提出來問題,為什麼不可以互換,很簡單解釋 Application 和Activity的上下文Context 不是同一個,繼承樹不同。Application 繼承自contextWrapper,而Activity繼承自ThemeWarpper. 還有Dialog彈窗和當前Activity繫結,自然不能使用Application的Context進行傳值。
引用