天天看點

android 中application context 和 activity Context 闡釋

前言

Context在開發Android應用的過程中扮演着非常重要的角色,

例如,啟動一個 Activity需要使用context.startActivity方法,

将一個xml檔案轉換為一個View對象也需要使用Context對象,

彈窗需要context,資源擷取需要,可以說沒有Context,android開發

無從談起

Applicaiton Context 和Activity Context 是否是同一個,能否混淆使用?

帶着這個問題接着看;

類結構圖

package android.content;      
android 中application context 和 activity Context 闡釋
package android.app;      
android 中application context 和 activity Context 闡釋

注意:

因為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 類,該類裡面羅列了所有初始化方法。

android 中application context 和 activity Context 闡釋

到這裡共說明了

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進行傳值。