1. 程式人生 > >android 中application context 和 activity Context 闡釋

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

引用