出自:http://blog.csdn.net/luoshengyang/article/details/8201936
在前文中,我們簡要介紹了Android應用程式視窗的框架。Android應用程式視窗在執行的過程中,需要訪問一些特定的資源或者類。這些特定的資源或者類構成了Android應用程式的執行上下文環境,Android應用程式視窗可以通過一個Context介面來訪問它,這個Context介面也是我們在開發應用程式時經常碰到的。在本文中,我們就將詳細分析Android應用程式視窗的執行上下文環境的建立過程。
在前面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應用程式視窗以及它的檢視的建立過程,敬請關注!
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!