1. 程式人生 > >Android應用程式視窗(Activity)的執行上下文環境(Context)的建立過程分析

Android應用程式視窗(Activity)的執行上下文環境(Context)的建立過程分析

               

        在前文中,我們簡要介紹了Android應用程式視窗的框架。Android應用程式視窗在執行的過程中,需要訪問一些特定的資源或者類。這些特定的資源或者類構成了Android應用程式的執行上下文環境,Android應用程式視窗可以通過一個Context介面來訪問它,這個Context介面也是我們在開發應用程式時經常碰到的。在本文中,我們就將詳細分析Android應用程式視窗的執行上下文環境的建立過程。

《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!

        在前面Android應用程式視窗(Activity)實現框架簡要介紹和學習計劃

一文中提到,Android應用程式視窗的執行上下文環境是通過ContextImpl類來描述的,即每一個Activity元件都關聯有一個ContextImpl物件。ContextImpl類繼承了Context類,它與Activity元件的關係如圖1所示:

圖1 ContextImpl類與Activity類的關係圖

        這個類圖在設計模式裡面就可以稱為裝飾模式。Activity元件通過其父類ContextThemeWrapper和ContextWrapper的成員變數mBase來引用了一個ContextImpl物件,這樣,Activity元件以後就可以通過這個ContextImpl物件來執行一些具體的操作,例如,

啟動Service元件註冊廣播接收者啟動Content Provider元件等操作。同時,ContextImpl類又通過自己的成員變數mOuterContext來引用了與它關聯的一個Activity元件,這樣,ContextImpl類也可以將一些操作轉發給Activity元件來處理。

        在前面Android應用程式啟動過程原始碼分析一文中,我們已經詳細分析過一個Activity元件的啟動過程了。在這個啟動過程中,最後一步便是通過ActivityThread類的成員函式performLaunchActivity在應用程式程序中建立一個Activity例項,並且為它設定執行上下文環境,即為它建立一個ContextImpl物件。接下來,我們就從ActivityThread類的成員函式performLaunchActivity開始,分析一個Activity例項的建立過程,以便可以從中瞭解它的執行上下文環境的建立過程,如圖2所示:

圖2 Android應用程式視窗的執行上下文環境的建立過程

        這個過程一共分為10個步驟,接下來我們就詳細分析每一個步驟。

        Step 1. ActivityThread.performLaunchActivity

public final class ActivityThread {    ......      Instrumentation mInstrumentation;    ......    private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {        ......        ComponentName component = r.intent.getComponent();        ......        Activity activity = null;        try {            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();            activity = mInstrumentation.newActivity(                    cl, component.getClassName(), r.intent);            ......        } catch (Exception e) {            ......        }        try {            Application app = r.packageInfo.makeApplication(false, mInstrumentation);            ......            if (activity != null) {                ContextImpl appContext = new ContextImpl();                ......                appContext.setOuterContext(activity);                ......                Configuration config = new Configuration(mConfiguration);                ......                activity.attach(appContext, this, getInstrumentation(), r.token,                        r.ident, app, r.intent, r.activityInfo, title, r.parent,                        r.embeddedID, r.lastNonConfigurationInstance,                        r.lastNonConfigurationChildInstances, config);                ......                mInstrumentation.callActivityOnCreate(activity, r.state);                ......              }            ......        } catch (SuperNotCalledException e) {            ......        } catch (Exception e) {            ......        }        return activity;    }}

        這個函式定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

        要啟動的Activity元件的類名儲存在變數component。有了這個類名之後,函式就可以呼叫ActivityThread類的成員變數mInstrumentation所描述一個Instrumentation物件的成員函式newActivity來建立一個Activity元件例項了,並且儲存變數activity中。Instrumentation類是用來記錄應用程式與系統的互動過程的,在接下來的Step 2中,我們再分析它的成員函式newActivity的實現。

       建立好了要啟動的Activity元件例項之後,函式接下來就可以對它進行初始化了。初始化一個Activity元件例項需要一個Application物件app、一個ContextImpl物件appContext以及一個Configuration物件config,它們分別用來描述該Activity元件例項的應用程式資訊、執行上下文環境以及配置資訊。這裡我們主要關心執行上下文環境的建立過程,即ContextImpl物件appContext的建立過程,這個過程我們在接下來的Step 4中再分析。

       ContextImpl物件appContext建立完成之後,函式就會呼叫它的成員函式setOuterContext來將與它所關聯的Activity元件例項activity儲存在它的內部。這樣,ContextImpl物件appContext以後就可以訪問與它所關聯的Activity元件的屬性或者方法。在接下來的Step 5中,我們再分析ContextImpl類的成員函式setOuterContext的實現。

       接著,函式就呼叫Activity元件例項activity的成員函式attach來將前面所建立的ContextImpl物件appContext以及Application物件app和Configuration物件config儲存在它的內部。這樣,Activity元件例項activity就可以訪問它的執行上下文環境資訊了。在接下來的Step 6中,我們再分析Activity類的成員函式attach的實現。

       最後,函式又通過呼叫ActivityThread類的成員變數mInstrumentation所描述一個Instrumentation物件的成員函式callActivityOnCreate來通知Activity元件例項activity,它已經被建立和啟動起來了。在接下來的Step 9中,我們再分析它的成員函式callActivityOnCreate的實現。

       接下來,我們就分別分析Instrumentation類的成員函式newActivity、ContextImpl類的建構函式以及成員函式setOuterContext、Activity類的成員函式attach和Instrumentation類的成員函式callActivityOnCreate的實現。

       Step 2. Instrumentation.newActivity

public class Instrumentation {    ......    public Activity newActivity(ClassLoader cl, String className,            Intent intent)            throws InstantiationException, IllegalAccessException,            ClassNotFoundException {        return (Activity)cl.loadClass(className).newInstance();    }    ......}
        這個函式定義在檔案frameworks/base/core/java/android/app/Instrumentation.java中。

        引數cl描述的是一個類載入器,而引數className描述的要載入的類。以className為引數來呼叫cl描述的是一個類載入器的成員函式loadClass,就可以得到一個Class物件。由於className描述的是一個Activity子類,因此,當函式呼叫前面得到的Class物件的成員函式newInstance的時候,就會建立一個Activity子類例項。這個Activity例項就是用來描述在前面Step 1中所要啟動的Activity元件的。

       Activity子類例項在建立的過程,會呼叫父類Activity的預設建構函式,以便可以完成Activity元件的建立過程。

       Step 3. new Activity

       Activity類定義在檔案frameworks/base/core/java/android/app/Activity.java中,它沒有定義自己的建構函式,因此,系統就會為它提供一個預設的建構函式。一般來說,一個類的建構函式是用來初始化該類的例項的,但是,系統為Activity類提供的預設建構函式什麼也不做,也就是說,Activity類例項在建立的時候,還沒有執行實質的初始化工作。這個初始化工作要等到Activity類的成員函式attach被呼叫的時候才會執行。在後面的Step 6中,我們就會看到Activity類的成員函式attach是如何初始化一個Activity類例項的。

      這一步執行完成之後,回到前面的Step 1中,即ActivityThread類的成員函式performLaunchActivity中,接下來就會呼叫ContextImpl類的建構函式來建立一個ContextImpl物件,以便可以用來描述正在啟動的Activity元件的執行上下文資訊。

      Step 4. new ContextImpl

class ContextImpl extends Context {    ......    private Context mOuterContext;    ......    ContextImpl() {        // For debug only        //++sInstanceCount;        mOuterContext = this;    }    ......}
       這個函式定義在檔案frameworks/base/core/java/android/app/ContextImpl.java中。

       ContextImpl類的成員變數mOuterContext的型別為Context。當一個ContextImpl物件是用來描述一個Activity元件的執行上下文環境時,那麼它的成員變數mOuterContext指向的就是該Activity元件。由於一個ContextImpl物件在建立的時候,並沒有引數用來指明它是用來描述一個Activity元件的執行上下文環境,因此,這裡就暫時將它的成員變數mOuterContext指向它自己。在接下來的Step 5中,我們就會看到,一個ContextImpl物件所關聯的一個Activity元件是通過呼叫ContextImpl類的成員函式setOuterContext來設定的。

      這一步執行完成之後,回到前面的Step 1中,即ActivityThread類的成員函式performLaunchActivity中,接下來就會呼叫ContextImpl類的成員函式setOuterContext來設定前面所建立一個ContextImpl物件所關聯的一個Activity元件,即正在啟動的Activity元件。

      Step 5. ContextImpl.setOuterContext

class ContextImpl extends Context {    ......    private Context mOuterContext;    ......    final void setOuterContext(Context context) {        mOuterContext = context;    }    ......}
       這個函式定義在檔案frameworks/base/core/java/android/app/ContextImpl.java中。

       引數context描述的是一個正在啟動的Activity元件,ContextImpl類的成員函式setOuterContext只是簡單地將它儲存在成員變數mContext中,以表明當前正在處理的一個ContextImpl物件是用來描述一個Activity元件的執行上下文環境的。

       這一步執行完成之後,回到前面的Step 1中,即ActivityThread類的成員函式performLaunchActivity中,接下來就會呼叫Activity類的成員函式attach來初始化正在啟動的Activity元件,其中,就包括設定正在啟動的Activity元件的執行上下文環境。

       Step 6. Activity.attach

public class Activity extends ContextThemeWrapper        implements LayoutInflater.Factory,        Window.Callback, KeyEvent.Callback,        OnCreateContextMenuListener, ComponentCallbacks {    ......    private Application mApplication;    ......    /*package*/ Configuration mCurrentConfig;    ......    private Window mWindow;    private WindowManager mWindowManager;    ......    final void attach(Context context, ActivityThread aThread,            Instrumentation instr, IBinder token, int ident,            Application application, Intent intent, ActivityInfo info,            CharSequence title, Activity parent, String id,            Object lastNonConfigurationInstance,            HashMap<String,Object> lastNonConfigurationChildInstances,            Configuration config) {        attachBaseContext(context);        mWindow = PolicyManager.makeNewWindow(this);        mWindow.setCallback(this);        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {            mWindow.setSoftInputMode(info.softInputMode);        }        ......        mApplication = application;        ......        mWindow.setWindowManager(null, mToken, mComponent.flattenToString());        ......        mWindowManager = mWindow.getWindowManager();        mCurrentConfig = config;    }    ......}

        這個函式定義在檔案frameworks/base/core/java/android/app/Activity.java中。

        函式首先呼叫從父類ContextThemeWrapper繼承下來的成員函式attachBaseConext來設定執行上下文環境,即將引數context所描述的一個ContextImpl物件儲存在內部。在接下來的Step 7中,我們再分析ContextThemeWrapper類的成員函式attachBaseConext的實現。

        函式接下來呼叫PolicyManager類的靜態成員函式makeNewWindow來建立了一個PhoneWindow,並且儲存在Activity類的成員變數mWindow中。這個PhoneWindow是用來描述當前正在啟動的應用程式視窗的。這個應用程式視窗在執行的過程中,會接收到一些事件,例如,鍵盤、觸控式螢幕事件等,這些事件需要轉發給與它所關聯的Activity元件處理,這個轉發操作是通過一個Window.Callback介面來實現的。由於Activity類實現了Window.Callback介面,因此,函式就可以將當前正在啟動的Activity元件所實現的一個Window.Callback介面設定到前面建立的一個PhoneWindow裡面去,這是通過呼叫Window類的成員函式setCallback來實現的。

       引數info指向的是一個ActivityInfo物件,用來描述當前正在啟動的Activity元件的資訊。其中,這個ActivityInfo物件的成員變數softInputMode用來描述當前正在啟動的一個Activity元件是否接受軟鍵盤輸入。如果接受的話,那麼它的值就不等於WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,並且描述的是當前正在啟動的Activity元件所接受的軟鍵盤輸入模式。這個軟鍵盤輸入模式設定到前面所建立的一個PhoneWindow物件內部去,這是通過呼叫Window類的成員函式setSoftInputMode來實現的。

       在Android系統中,每一個應用程式視窗都需要由一個視窗管理者來管理,因此,函式再接下來就會呼叫前面所建立的一個PhoneWindow物件從父類Window繼承下來的成員函式setWindowManager來為它設定一個合適的視窗管理者。這個視窗管理者設定完成之後,就可以通過呼叫Window類的成員函式getWindowManager來獲得。獲得這個視窗管理者之後,函式就將它儲存在Activity類的成員變數mWindowManager中。這樣,當前正在啟動的Activity元件以後就可以通過它的成員變數mWindowManager來管理與它所關聯的視窗。

       除了建立和初始化一個PhoneWindow之外,函式還會分別把引數application和config所描述的一個Application物件和一個Configuration物件儲存在Activity類的成員變數mApplication和mCurrentConfig中。這樣,當前正在啟動的Activity元件就可以訪問它的應用程式資訊以及配置資訊。

      在接下來的一篇文章中,我們再詳細分析PolicyManager類的靜態成員函式makeNewWindow,以及Window類的成員函式setCallback、setSoftInputMode和setWindowManager的實現,以便可以瞭解應用程式視窗的建立過程。

      接下來,我們繼續分析ContextThemeWrapper類的成員函式attachBaseConext的實現,以便可以繼續瞭解一個應用程式視窗的執行上下文環境的設定過程。

      Step 7. ContextThemeWrapper.attachBaseConext

public class ContextThemeWrapper extends ContextWrapper {    private Context mBase;    ......    @Override protected void attachBaseContext(Context newBase) {        super.attachBaseContext(newBase);        mBase = newBase;    }    ......}
        這個函式定義在檔案frameworks/base/core/java/android/view/ContextThemeWrapper.java中。

        ContextThemeWrapper類用來維護一個應用程式視窗的主題,而用來描述這個應用程式視窗的執行上下文環境的一個ContextImpl物件就儲存在ContextThemeWrapper類的成員函式mBase中。

        ContextThemeWrapper類的成員函式attachBaseConext的實現很簡單,它首先呼叫父類ContextWrapper的成員函式attachBaseConext來將引數newBase所描述的一個ContextImpl物件儲存到父類ContextWrapper中去,接著再將這個ContextImpl物件儲存在ContextThemeWrapper類的成員變數mBase中。

        接下來,我們就繼續分析ContextWrapper類的成員函式attachBaseConext的實現。

        Step 8. ContextWrapper.attachBaseConext

public class ContextWrapper extends Context {    Context mBase;    ......    protected void attachBaseContext(Context base) {        if (mBase != null) {            throw new IllegalStateException("Base context already set");        }        mBase = base;    }    ......}
        這個函式定義在檔案frameworks/base/core/java/android/content/ContextWrapper.java 中。

        ContextWrapper類只是一個代理類,它只是簡單地封裝了對其成員變數mBase所描述的一個Context物件的操作。ContextWrapper類的成員函式attachBaseConext的實現很簡單,它只是將引數base所描述的一個ContextImpl物件儲存在成員變數mBase中。這樣,ContextWrapper類就可以將它的功能交給ContextImpl類來具體實現。

        這一步執行完成之後,當前正在啟動的Activity元件的執行上下文環境就設定完成了,回到前面的Step 1中,即ActivityThread類的成員函式performLaunchActivity中,接下來就會呼叫Instrumentation類的成員函式callActivityOnCreate來通知當前正在啟動的Activity元件,它已經建立和啟動完成了。

        Step 9. Instrumentation.callActivityOnCreate

public class Instrumentation {    ......    public void callActivityOnCreate(Activity activity, Bundle icicle) {        ......        activity.onCreate(icicle);        ......    }     ......}
       這個函式定義在檔案frameworks/base/core/java/android/app/Instrumentation.java中。

       函式主要就是呼叫當前正在啟動的Activity元件的成員函式onCreate,用來通知它已經成功地建立和啟動完成了。

      Step 10. Activity.onCreate

public class Activity extends ContextThemeWrapper        implements LayoutInflater.Factory,        Window.Callback, KeyEvent.Callback,        OnCreateContextMenuListener, ComponentCallbacks {    ......    boolean mCalled;    ......    /*package*/ boolean mVisibleFromClient = true;    ......        protected void onCreate(Bundle savedInstanceState) {        mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(                com.android.internal.R.styleable.Window_windowNoDisplay, false);        mCalled = true;    }     ......}

       這個函式定義在檔案frameworks/base/core/java/android/app/Activity.java中。

       一般來說,我們都是通過定義一個Activity子類來實現一個Activity元件的。重寫父類Activity的某些成員函式的時候,必須要回調父類Activity的這些成員函式。例如,當Activity子類在重寫父類Activity的成員函式onCreate時,就必須回撥父類Activity的成員函式onCreate。這些成員函式被回調了之後,Activity類就會將其成員變數mCalled的值設定為true。這樣,Activity類就可以通過其成員變數mCalled來檢查其子類在重寫它的某些成員函式時,是否正確地回調了父類的這些成員函式。

       Activity類的另外一個成員變數mVisibleFromClient用來描述一個應用程式視窗是否是可見的。如果是可見的,那麼它的值就會等於true。當Activity類的成員函式onCreate被其子類回撥時,它就會檢查對應的應用程式視窗的主題屬性android:windowNoDisplay的值是否等於true。如果等於true的話,那麼就說明當前所啟動的應用程式視窗是不可見的,這時候Activity類的成員變數mVisibleFromClient的值就會被設定為false,否則的話,就會被設定為true。

      Activity子類在重寫成員函式onCreate的時候,一般都會呼叫父類Activity的成員函式setContentView來為為當前正啟動的應用程式視窗建立檢視(View)。在接下來的文章中,我們再詳細描述應用程式視窗的檢視的建立過程。

      至此,一個Activity元件的建立過程,以及它的執行上下文環境的建立過程,就分析完成了。這個過程比較簡單,我們是從中獲得以下三點資訊:

      1. 一個Android應用視窗的執行上下文環境是使用一個ContextImpl物件來描述的,這個ContextImpl物件會分別儲存在Activity類的父類ContextThemeWrapper和ContextWrapper的成員變數mBase中,即ContextThemeWrapper類和ContextWrapper類的成員變數mBase指向的是一個ContextImpl物件。

      2. Activity元件在建立過程中,即在它的成員函式attach被呼叫的時候,會建立一個PhoneWindow物件,並且儲存在成員變數mWindow中,用來描述一個具體的Android應用程式視窗。

      3.  Activity元件在建立的最後,即在它的子類所重寫的成員函式onCreate中,會呼叫父類Activity的成員函式setContentView來建立一個Android應用程式視窗的檢視。

      在接下來的兩篇文章中,我們就將會詳細描述Android應用程式視窗以及它的檢視的建立過程,敬請關注!