天天看點

Android應用程式視窗(Activity)的視窗對象(Window)的建立過程分析

      在前文中,我們分析了Android應用程式視窗的運作上下文環境的建立過程。由此可知,每一個Activity元件都有一個關聯的ContextImpl對象,同時,它還關聯有一個Window對象,用來描述一個具體的應用程式視窗。由此又可知,Activity隻不過是一個高度抽象的UI元件,它的具體UI實作其實是由其它的一系列對象來實作的。在本文中,我們就将詳細分析Android應用程式視窗對象的建立過程。

圖1 Activity和Window的類關系圖

圖2 Window和PhoneWindow的類關系圖

圖3 Android應用程式視窗的建立過程

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

      Step 1. Activity.attach

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<code>public</code> <code>class</code> <code>Activity </code><code>extends</code> <code>ContextThemeWrapper</code>

<code>        </code><code>implements</code> <code>LayoutInflater.Factory,</code>

<code>        </code><code>Window.Callback, KeyEvent.Callback,</code>

<code>        </code><code>OnCreateContextMenuListener, ComponentCallbacks {</code>

<code>    </code><code>...... </code>

<code>                                                      </code> 

<code>    </code><code>private</code> <code>Window mWindow; </code>

<code>    </code><code>......</code>

<code>    </code><code>final</code> <code>void</code> <code>attach(Context context, ActivityThread aThread,</code>

<code>            </code><code>Instrumentation instr, IBinder token, </code><code>int</code> <code>ident,</code>

<code>            </code><code>Application application, Intent intent, ActivityInfo info,</code>

<code>            </code><code>CharSequence title, Activity parent, String id,</code>

<code>            </code><code>Object lastNonConfigurationInstance,</code>

<code>            </code><code>HashMap&lt;String,Object&gt; lastNonConfigurationChildInstances,</code>

<code>            </code><code>Configuration config) {</code>

<code>        </code><code>......</code>

<code>        </code><code>mWindow = PolicyManager.makeNewWindow(</code><code>this</code><code>);</code>

<code>        </code><code>mWindow.setCallback(</code><code>this</code><code>);</code>

<code>        </code><code>if</code> <code>(info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {</code>

<code>            </code><code>mWindow.setSoftInputMode(info.softInputMode);</code>

<code>        </code><code>}</code>

<code>        </code><code>mWindow.setWindowManager(</code><code>null</code><code>, mToken, mComponent.flattenToString());</code>

<code>                                                        </code> 

<code>    </code><code>}</code>

<code>}</code>

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

       函數首先調用PolicyManager類的靜态成員函數makeNewWindow來建立一個類型為PhoneWindow的應用程式視窗,并且儲存在Activity類的成員變量mWindow中。有了這個類型為PhoneWindow的應用程式視窗,函數接下來還會調用它的成員函數setCallback、setSoftInputMode和setWindowManager來設定視窗回調接口、軟鍵盤輸入區域的顯示模式和本地視窗管理器。

       PhoneWindow類的成員函數setCallback、setSoftInputMode和setWindowManager都是從父類Window繼承下來的,是以,接下來我們就繼續分析PolicyManager類的靜态成員函數makeNewWindow,以及Window類的成員函數setCallback、setSoftInputMode和setWindowManager的實作。

       Step 2. PolicyManager.makeNewWindow

<code>public</code> <code>final</code> <code>class</code> <code>PolicyManager {</code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>String POLICY_IMPL_CLASS_NAME =</code>

<code>        </code><code>"com.android.internal.policy.impl.Policy"</code><code>;</code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>IPolicy sPolicy;</code>

<code>    </code><code>static</code> <code>{</code>

<code>        </code><code>// Pull in the actual implementation of the policy at run-time</code>

<code>        </code><code>try</code> <code>{</code>

<code>            </code><code>Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);</code>

<code>            </code><code>sPolicy = (IPolicy)policyClass.newInstance();</code>

<code>        </code><code>} </code><code>catch</code> <code>(ClassNotFoundException ex) {</code>

<code>            </code><code>throw</code> <code>new</code> <code>RuntimeException(</code>

<code>                    </code><code>POLICY_IMPL_CLASS_NAME + </code><code>" could not be loaded"</code><code>, ex);</code>

<code>        </code><code>} </code><code>catch</code> <code>(InstantiationException ex) {</code>

<code>                    </code><code>POLICY_IMPL_CLASS_NAME + </code><code>" could not be instantiated"</code><code>, ex);</code>

<code>        </code><code>} </code><code>catch</code> <code>(IllegalAccessException ex) {</code>

<code>    </code><code>// The static methods to spawn new policy-specific objects</code>

<code>    </code><code>public</code> <code>static</code> <code>Window makeNewWindow(Context context) {</code>

<code>        </code><code>return</code> <code>sPolicy.makeNewWindow(context);</code>

      這個函數定義在檔案frameworks/base/core/java/com/android/internal/policy/PolicyManager.java中。

      PolicyManager是一個視窗管理政策類,它在第一次被使用的時候,就會建立一個Policy類執行個體,并且儲存在靜态成員變量sPolicy中,以後PolicyManager類的視窗管理政策就是通過這個Policy類執行個體來實作的,例如,PolicyManager類的靜态成員函數makeNewWindow就是通過調用這個Policy類執行個體的成員函數makeNewWindow來建立一個具體的應用程式視窗的。

      接下來,我們就繼續分析Policy類的成員函數makeNewWindow的實作。

      Step 3. Policy.makeNewWindow

<code>public</code> <code>class</code> <code>Policy </code><code>implements</code> <code>IPolicy {</code>

<code>    </code><code>public</code> <code>PhoneWindow makeNewWindow(Context context) {</code>

<code>        </code><code>return</code> <code>new</code> <code>PhoneWindow(context);</code>

<code>                                           </code> 

       這個函數定義在檔案frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java中。

       Policy類的成員函數makeNewWindow的實作很簡單,它隻是建立了一個PhoneWindow對象,然後傳回給調用者。

       接下來,我們就繼續分析PhoneWindow類的構造函數的實作,以便可以了解一個類型為PhoneWindow的應用程式視窗的建立過程。

       Step 4. new PhoneWindow

<code>public</code> <code>class</code> <code>PhoneWindow </code><code>extends</code> <code>Window </code><code>implements</code> <code>MenuBuilder.Callback {</code>

<code>    </code><code>// This is the top-level view of the window, containing the window decor.</code>

<code>    </code><code>private</code> <code>DecorView mDecor;</code>

<code>    </code><code>// This is the view in which the window contents are placed. It is either</code>

<code>    </code><code>// mDecor itself, or a child of mDecor where the contents go.</code>

<code>    </code><code>private</code> <code>ViewGroup mContentParent;</code>

<code>    </code><code>private</code> <code>LayoutInflater mLayoutInflater;</code>

<code>    </code><code>public</code> <code>PhoneWindow(Context context) {</code>

<code>        </code><code>super</code><code>(context);</code>

<code>        </code><code>mLayoutInflater = LayoutInflater.from(context);</code>

      這個函數定義在檔案frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

      PhoneWindow類的構造函數很簡單,它首先調用父類Window的構造函數來執行一些初始化操作,接着再調用LayoutInflater的靜态成員函數from建立一個LayoutInflater執行個體,并且儲存在成員變量mLayoutInflater中。這樣,PhoneWindow類以後就可以通過成員變量mLayoutInflater來建立應用程式視窗的視圖,這個視圖使用類型為DecorView的成員變量mDecor來描述。PhoneWindow類還有另外一個類型為ViewGroup的成員變量mContentParent,用來描述一個視圖容器,這個容器存放的就是成員變量mDecor所描述的視圖的内容,不過這個容器也有可能指向的是mDecor本身。在後面的文章中,我們再詳細分析類型為PhoneWindow的應用程式視窗的視圖的建立過程。

     Window的構造函數定義在檔案frameworks/base/core/java/android/view/Window.java中,它的實作很簡單,隻是初始化了其成員變量mContext,如下所示:

<code>public</code> <code>abstract</code> <code>class</code> <code>Window {</code>

<code>    </code><code>private</code> <code>final</code> <code>Context mContext;</code>

<code>    </code><code>public</code> <code>Window(Context context) {</code>

<code>        </code><code>mContext = context;</code>

<code>                                    </code> 

      從前面的調用過程可以知道,參數context描述的是正在啟動的Activity元件,将它儲存在Window類的成員變量mContext之後,Window類就可以通過它來通路與Activity元件相關的資源了。

      這一步執行完成之後,回到前面的Step 1中,即Activity類的成員函數attach中,接下來就會繼續調用前面所建立的PhoneWindow對象從父類Window繼承下來的成員函數setCallback來設定視窗回調接口,是以,接下來我們就繼續分析Window類的成員函數setCallback的實作。

      Step 5. Window.setCallback

<code>    </code><code>private</code> <code>Callback mCallback;</code>

<code>    </code><code>/**</code>

<code>     </code><code>* Set the Callback interface for this window, used to intercept key</code>

<code>     </code><code>* events and other dynamic operations in the window.</code>

<code>     </code><code>*</code>

<code>     </code><code>* @param callback The desired Callback interface.</code>

<code>     </code><code>*/</code>

<code>    </code><code>public</code> <code>void</code> <code>setCallback(Callback callback) {</code>

<code>        </code><code>mCallback = callback;</code>

<code>                                </code> 

       這個函數定義在檔案frameworks/base/core/java/android/view/Window.java中。

       這一步執行完成之後,回到前面的Step 1中,即Activity類的成員函數attach中,接下來就會繼續調用前面所建立的PhoneWindow對象從父類Window繼承下來的成員函數setSoftInputMode來設定應用程式視窗的軟鍵盤輸入區域的顯示模式,是以,接下來我們就繼續分析Window類的成員函數setSoftInputMode的實作。

       Step 6. Window.setSoftInputMode

<code>    </code><code>private</code> <code>boolean</code> <code>mHasSoftInputMode = </code><code>false</code><code>;</code>

<code>    </code><code>public</code> <code>void</code> <code>setSoftInputMode(</code><code>int</code> <code>mode) {</code>

<code>        </code><code>final</code> <code>WindowManager.LayoutParams attrs = getAttributes();</code>

<code>        </code><code>if</code> <code>(mode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {</code>

<code>            </code><code>attrs.softInputMode = mode;</code>

<code>            </code><code>mHasSoftInputMode = </code><code>true</code><code>;</code>

<code>        </code><code>} </code><code>else</code> <code>{</code>

<code>            </code><code>mHasSoftInputMode = </code><code>false</code><code>;</code>

<code>        </code><code>if</code> <code>(mCallback != </code><code>null</code><code>) {</code>

<code>            </code><code>mCallback.onWindowAttributesChanged(attrs);</code>

      這個函數定義在檔案frameworks/base/core/java/android/view/Window.java中。

      參數mode有SOFT_INPUT_STATE_UNSPECIFIED、SOFT_INPUT_STATE_UNCHANGED、SOFT_INPUT_STATE_HIDDEN、SOFT_INPUT_STATE_ALWAYS_HIDDEN、SOFT_INPUT_STATE_VISIBLE和SOFT_INPUT_STATE_ALWAYS_VISIBLE一共六個取值,用來描述視窗的軟鍵盤輸入區域的顯示模式,它們的含義如下所示:

     1. SOFT_INPUT_STATE_UNSPECIFIED:沒有指定軟鍵盤輸入區域的顯示狀态。

     2. SOFT_INPUT_STATE_UNCHANGED:不要改變軟鍵盤輸入區域的顯示狀态。

     3. SOFT_INPUT_STATE_HIDDEN:在合适的時候隐藏軟鍵盤輸入區域,例如,當使用者導航到目前視窗時。

     4. SOFT_INPUT_STATE_ALWAYS_HIDDEN:當視窗獲得焦點時,總是隐藏軟鍵盤輸入區域。

     5. SOFT_INPUT_STATE_VISIBLE:在合适的時候顯示軟鍵盤輸入區域,例如,當使用者導航到目前視窗時。

     6. SOFT_INPUT_STATE_ALWAYS_VISIBLE:當視窗獲得焦點時,總是顯示軟鍵盤輸入區域。

     當參數mode的值不等于SOFT_INPUT_STATE_UNSPECIFIED時,就表示目前視窗被指定軟鍵盤輸入區域的顯示模式,這時候Window類的成員函數setSoftInputMode就會将成員變量mHasSoftInputMode的值設定為true,并且将這個顯示模式儲存在用來描述視窗布局屬性的一個WindowManager.LayoutParams對象的成員變量softInputMode中,否則的話,就會将成員變量mHasSoftInputMode的值設定為false。

     設定完成視窗的軟鍵盤輸入區域的顯示模式之後,如果Window類的成員變量mCallback指向了一個視窗回調接口,那麼Window類的成員函數setSoftInputMode還會調用它的成員函數onWindowAttributesChanged來通知與視窗所關聯的Activity元件,它的視窗布局屬性發生了變化。

      這一步執行完成之後,回到前面的Step 1中,即Activity類的成員函數attach中,接下來就會繼續調用前面所建立的PhoneWindow對象從父類Window繼承下來的成員函數setWindowManager來設定應用程式視窗的本地視窗管理器,是以,接下來我們就繼續分析Window類的成員函數setWindowManager的實作。

      Step 7. Window.setWindowManager

<code>    </code><code>private</code> <code>WindowManager mWindowManager;</code>

<code>    </code><code>private</code> <code>IBinder mAppToken;</code>

<code>    </code><code>private</code> <code>String mAppName;</code>

<code>    </code><code>public</code> <code>void</code> <code>setWindowManager(WindowManager wm,</code>

<code>            </code><code>IBinder appToken, String appName) {</code>

<code>        </code><code>mAppToken = appToken;</code>

<code>        </code><code>mAppName = appName;</code>

<code>        </code><code>if</code> <code>(wm == </code><code>null</code><code>) {</code>

<code>            </code><code>wm = WindowManagerImpl.getDefault();</code>

<code>        </code><code>mWindowManager = </code><code>new</code> <code>LocalWindowManager(wm);</code>

      參數appName用來描述目前正在處理的視窗所關聯的Activity元件的名稱,這個名稱會被儲存在Window類的成員變量mAppName中。

      參數wm用來描述一個視窗管理器。從前面的調用過程可以知道, 這裡傳進來的參數wm的值等于null,是以,函數首先會調用WindowManagerImpl類的靜态成員函數getDefault來獲得一個預設的視窗管理器。有了這個視窗管理器之後,函數接着再使用它來建立一個本地視窗管理器,即一個LocalWindowManager對象,用來維護目前正在處理的應用程式視窗。

      接下來,我們首先分析WindowManagerImpl類的靜态成員函數getDefault的實作,接着再分析本地視窗管理器的建立過程,即LocalWindowManager類的構造函數的實作。

      Step 8. WindowManagerImpl.getDefault

<code>public</code> <code>class</code> <code>WindowManagerImpl </code><code>implements</code> <code>WindowManager {</code>

<code>    </code><code>public</code> <code>static</code> <code>WindowManagerImpl getDefault()</code>

<code>    </code><code>{</code>

<code>        </code><code>return</code> <code>mWindowManager;</code>

<code>               </code> 

<code>    </code><code>private</code> <code>static</code> <code>WindowManagerImpl mWindowManager = </code><code>new</code> <code>WindowManagerImpl();</code>

     這個函數定義在檔案frameworks/base/core/java/android/view/WindowManagerImpl.java中。

      WindowManagerImpl類的靜态成員函數getDefault的實作很簡單,它隻是将靜态成員變量mWindowManager所指向的一個WindowManagerImpl對象傳回給調用者,這個WindowManagerImpl對象實作了WindowManager接口,是以,它就可以用來管理應用程式視窗。

     這一步執行完成之後,回到前面的Step 7中,即Window類的成員函數setWindowManager中,接下來就會使用前面所獲得一個WindowManagerImpl對象來建立一個本地視窗管理器,即一個LocalWindowManager對象。

     Step 9. new LocalWindowManager

<code>    </code><code>private</code> <code>class</code> <code>LocalWindowManager </code><code>implements</code> <code>WindowManager {</code>

<code>        </code><code>LocalWindowManager(WindowManager wm) {</code>

<code>            </code><code>mWindowManager = wm;</code>

<code>            </code><code>mDefaultDisplay = mContext.getResources().getDefaultDisplay(</code>

<code>                    </code><code>mWindowManager.getDefaultDisplay());</code>

<code>        </code><code>private</code> <code>final</code> <code>WindowManager mWindowManager;</code>

<code>        </code><code>private</code> <code>final</code> <code>Display mDefaultDisplay;</code>

      LocalWindowManager類的構造函數首先将參數wm所描述的一個WindowManagerImpl對象儲存它的成員變量mWindowManager中,這樣以後就将視窗管理工作交給它來處理。

      LocalWindowManager類的構造函數接着又通過成員變量mWindowManager所描述的一個WindowManagerImpl對象的成員函數getDefaultDisplay來獲得一個Display對象,用來描述系統螢幕屬性。

      由于前面所獲得的Display對象描述的是全局的螢幕屬性,而目前正在處理的視窗可能配置了一些可自定義的螢幕屬性,是以,LocalWindowManager類的構造函數需要進一步地調整前面所獲得的Display對象所描述的螢幕屬性,以便可以适合目前正在處理的視窗使用。LocalWindowManager類的構造函數首先通過外部類Window的成員變量mContext的成員函數getResources來獲得一個Resources對象,接着再調用這個Resources對象的成員函數getDefaultDisplay來調整前面所獲得的Display對象所描述的螢幕屬性。最終調整完成的Display對象就儲存在LocalWindowManager類的成員變量mDefaultDisplay中。

      從前面的Step 4可以知道,類Window的成員變量mContext描述的是與目前視窗所關聯的一個Activity元件。Activity類的成員函數getResources是從父類ContextWrapper繼續下來的,它實作在檔案frameworks/base/core/java/android/content/ContextWrapper.java中,如下所示:

<code>public</code> <code>class</code> <code>ContextWrapper </code><code>extends</code> <code>Context {</code>

<code>    </code><code>Context mBase;</code>

<code>    </code><code>@Override</code>

<code>    </code><code>public</code> <code>Resources getResources()</code>

<code>        </code><code>return</code> <code>mBase.getResources();</code>

      至此,我們就分析完成一個Activity元件所關聯的應用程式視窗對象的建立過程了。從分析的過程可以知道:

     1. 一個Activity元件所關聯的應用程式視窗對象的類型為PhoneWindow。

     2. 這個類型為PhoneWindow的應用程式視窗是通過一個類型為LocalWindowManager的本地視窗管理器來維護的。

     3. 這個類型為LocalWindowManager的本地視窗管理器又是通過一個類型為WindowManagerImpl的視窗管理器來維護應用程式視窗的。

     4. 這個類型為PhoneWindow的應用程式視窗内部有一個類型為DecorView的視圖對象,這個視圖對象才是真正用來描述一個Activity元件的UI的。

     在接下來的一篇文章中,我們将繼續分析應用程式視窗内部的視圖對象的建立過程,敬請關注!

本文轉自 Luoshengyang 51CTO部落格,原文連結:http://blog.51cto.com/shyluo/1242879,如需轉載請自行聯系原作者

繼續閱讀