天天看点

Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

 从前文可知道,每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图。应用程序窗口视图是真正用来实现UI内容和布局的,也就是说,每一个Activity组件的UI内容和布局都是通过与其所关联的一个Window对象的内部的一个View对象来实现的。在本文中,我们就详细分析应用程序窗口视图的创建过程。

图1 DecorView类的实现

图2 应用程序窗口视图与ViewRoot的关系图

       简单来说,ViewRoot相当于是MVC模型中的Controller,它有以下职责:

       1. 负责为应用程序窗口视图创建Surface。

       2. 配合WindowManagerService来管理系统的应用程序窗口。

       3. 负责管理、布局和渲染应用程序窗口视图的UI。

图3 应用程序窗口视图的创建过程

       这个过程一共可以分为13个步骤,接下来我们就详细分析每一个步骤。

       Step 1. ActivityThread.handleLaunchActivity

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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

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

<code>    </code><code>private</code> <code>final</code> <code>void</code> <code>handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {</code>

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

<code>        </code><code>Activity a = performLaunchActivity(r, customIntent);</code>

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

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

<code>            </code><code>handleResumeActivity(r.token, </code><code>false</code><code>, r.isForward);</code>

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

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

<code>}</code>

       这个函数定义在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。

       函数首先调用ActivityThread类的成员函数performLaunchActivity来创建要启动的Activity组件。在创建Activity组件的过程中,还会为该Activity组件创建窗口对象和视图对象。Activity组件创建完成之后,就可以将它激活起来了,这是通过调用ActivityThread类的成员函数handleResumeActivity来执行的。

       接下来,我们首先分析ActivityThread类的成员函数performLaunchActivity的实现,以便可以了解应用程序窗口视图对象的创建过程,接着再回过头来继续分析ActivityThread类的成员函数handleResumeActivity的实现,以便可以了解与应用程序窗口视图对象所关联的ViewRoot对象的创建过程。

       Step 2. ActivityThread.performLaunchActivity

       Step 3. Activity.onCreate

       这个函数定义在文件frameworks/base/core/java/android/app/Activity.java中。

<code>public</code> <code>class</code> <code>Hello </code><code>extends</code> <code>Activity </code><code>implements</code> <code>OnClickListener {</code>

<code>                                                                </code> 

<code>    </code><code>/** Called when the activity is first created. */</code>

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

<code>    </code><code>public</code> <code>void</code> <code>onCreate(Bundle savedInstanceState) {</code>

<code>        </code><code>super</code><code>.onCreate(savedInstanceState);</code>

<code>        </code><code>setContentView(R.layout.main);</code>

<code>                                                            </code> 

      其中,调用从父类Activity继承下来的成员函数setContentView就是用来创建应用程序窗口视图对象的。

      接下来,我们就继续分析Activity类的成员函数setContentView的实现。

      Step 4. Activity.setContentView

15

16

<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>private</code> <code>Window mWindow;</code>

<code>    </code><code>public</code> <code>Window getWindow() {</code>

<code>        </code><code>return</code> <code>mWindow;</code>

<code>    </code><code>public</code> <code>void</code> <code>setContentView(</code><code>int</code> <code>layoutResID) {</code>

<code>        </code><code>getWindow().setContentView(layoutResID);</code>

       Activity类的成员函数setContentView首先调用另外一个成员函数getWindow来获得成员变量mWindow所描述的一个窗口对象,接着再调用这个窗口对象的成员函数setContentView来执行创建应用程序窗口视图对象的工作。

       Step 5. PhoneWindow.setContentView

17

18

19

20

21

<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 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>if</code> <code>(mContentParent == </code><code>null</code><code>) {</code>

<code>            </code><code>installDecor();</code>

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

<code>            </code><code>mContentParent.removeAllViews();</code>

<code>        </code><code>mLayoutInflater.inflate(layoutResID, mContentParent);</code>

<code>        </code><code>final</code> <code>Callback cb = getCallback();</code>

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

<code>            </code><code>cb.onContentChanged();</code>

       这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

       PhoneWindow类的成员变量mContentParent用来描述一个类型为DecorView的视图对象,或者这个类型为DecorView的视图对象的一个子视图对象,用作UI容器。当它的值等于null的时候,就说明正在处理的应用程序窗口的视图对象还没有创建。在这种情况下,就会调用成员函数installDecor来创建应用程序窗口视图对象。否则的话,就说明是要重新设置应用程序窗口的视图。在重新设置之前,首先调用成员变量mContentParent所描述的一个ViewGroup对象来移除原来的UI内空。

     接下来,我们就继续分析PhoneWindow类的成员函数installDecor的实现,以便可以继续了解应用程序窗口视图对象的创建过程。

     Step 6. PhoneWindow.installDecor

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

<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>private</code> <code>TextView mTitleView;</code>

<code>    </code><code>private</code> <code>CharSequence mTitle = </code><code>null</code><code>;</code>

<code>    </code><code>private</code> <code>void</code> <code>installDecor() {</code>

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

<code>            </code><code>mDecor = generateDecor();</code>

<code>            </code><code>mContentParent = generateLayout(mDecor);</code>

<code>            </code><code>mTitleView = (TextView)findViewById(com.android.internal.R.id.title);</code>

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

<code>                </code><code>if</code> <code>((getLocalFeatures() &amp; (</code><code>1</code> <code>&lt;&lt; FEATURE_NO_TITLE)) != </code><code>0</code><code>) {</code>

<code>                    </code><code>View titleContainer = findViewById(com.android.internal.R.id.title_container);</code>

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

<code>                        </code><code>titleContainer.setVisibility(View.GONE);</code>

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

<code>                        </code><code>mTitleView.setVisibility(View.GONE);</code>

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

<code>                    </code><code>if</code> <code>(mContentParent </code><code>instanceof</code> <code>FrameLayout) {</code>

<code>                        </code><code>((FrameLayout)mContentParent).setForeground(</code><code>null</code><code>);</code>

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

<code>                    </code><code>mTitleView.setText(mTitle);</code>

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

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

       由于我们是在Activity组件启动的过程中创建应用程序窗口视图的,因此,我们同时假设此时PhoneWindow类的成员变量mDecor的值等于null。这时候PhoneWindow类的成员函数installDecor就会调用另外一个成员函数generateDecor来创建一个DecorView对象,并且保存在PhoneWindow类的成员变量mDecor中。

       PhoneWindow类的成员函数installDecor接着再调用另外一个成员函数generateLayout来根据当前应用程序窗口的Feature来加载对应的窗口布局文件。这些布局文件保存在frameworks/base/core/res/res/layout目录下,它们必须包含有一个id值为“content”的布局控件。这个布局控件必须要从ViewGroup类继承下来,用来作为窗口的UI容器。PhoneWindow类的成员函数generateLayout执行完成之后,就会这个id值为“content”的ViewGroup控件来给PhoneWindow类的成员函数installDecor,后者再将其保存在成员变量mContentParent中。

      PhoneWindow类的成员函数installDecor还会检查前面加载的窗口布局文件是否包含有一个id值为“title”的TextView控件。如果包含有的话,就会将它保存在PhoneWindow类的成员变量mTitleView中,用来描述当前应用程序窗口的标题栏。但是,如果当前应用程序窗口是没有标题栏的,即它的Feature位FEATURE_NO_TITLE的值等于1,那么PhoneWindow类的成员函数installDecor就需要将前面得到的标题栏隐藏起来。注意,PhoneWindow类的成员变量mTitleView所描述的标题栏有可能是包含在一个id值为“title_container”的容器里面的,在这种情况下,就需要隐藏该标题栏容器。另一方面,如果当前应用程序窗口是设置有标题栏的,那么PhoneWindow类的成员函数installDecor就会设置它的标题栏文字。应用程序窗口的标题栏文字保存在PhoneWindow类的成员变量mTitle中,我们可以调用PhoneWindow类的成员函数setTitle来设置。

      这一步执行完成之后,应用程序窗口视图就创建完成了,回到前面的Step 1中,即ActivityThread类的成员函数handleLaunchActivity中,接下来就会调用ActivityThread类的另外一个成员函数handleResumeActivity来激活正在启动的Activity组件。由于在是第一次激活该Activity组件,因此,在激活之前,还会为该Activity组件创建一个ViewRoot对象,并且与前面所创建的应用程序窗口视图关联起来,以便后面可以通过该ViewRoot对象来控制应用程序窗口视图的UI展现。

      接下来,我们就继续分析ActivityThread类的成员函数handleResumeActivity的实现。

      Step 7. ActivityThread.handleResumeActivity

<code>    </code><code>final</code> <code>void</code> <code>handleResumeActivity(IBinder token, </code><code>boolean</code> <code>clearHide, </code><code>boolean</code> <code>isForward) {</code>

<code>        </code><code>ActivityClientRecord r = performResumeActivity(token, clearHide);</code>

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

<code>            </code><code>final</code> <code>Activity a = r.activity;</code>

<code>            </code><code>// If the window hasn't yet been added to the window manager,</code>

<code>            </code><code>// and this guy didn't finish itself or start another activity,</code>

<code>            </code><code>// then go ahead and add the window.</code>

<code>            </code><code>boolean</code> <code>willBeVisible = !a.mStartedActivity;</code>

<code>            </code><code>if</code> <code>(!willBeVisible) {</code>

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

<code>                    </code><code>willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(</code>

<code>                            </code><code>a.getActivityToken());</code>

<code>                </code><code>} </code><code>catch</code> <code>(RemoteException e) {</code>

<code>            </code><code>if</code> <code>(r.window == </code><code>null</code> <code>&amp;&amp; !a.mFinished &amp;&amp; willBeVisible) {</code>

<code>                </code><code>r.window = r.activity.getWindow();</code>

<code>                </code><code>View decor = r.window.getDecorView();</code>

<code>                </code><code>decor.setVisibility(View.INVISIBLE);</code>

<code>                </code><code>ViewManager wm = a.getWindowManager();</code>

<code>                </code><code>WindowManager.LayoutParams l = r.window.getAttributes();</code>

<code>                </code><code>a.mDecor = decor;</code>

<code>                </code><code>l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;</code>

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

<code>                </code><code>if</code> <code>(a.mVisibleFromClient) {</code>

<code>                    </code><code>a.mWindowAdded = </code><code>true</code><code>;</code>

<code>                    </code><code>wm.addView(decor, l);</code>

<code>                                        </code> 

       这个函数定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

       ActivityThread类的成员函数handleResumeActivity首先调用另外一个成员函数performResumeActivity来通知Activity组件,它要被激活了,即会导致Activity组件的成员函数onResume被调用。ActivityThread类的成员函数performResumeActivity的返回值是一个ActivityClientRecord对象r,这个ActivityClientRecord对象的成员变量activity描述的就是正在激活的Activity组件a。

       ActivityThread类的成员函数handleResumeActivity接下来判断正在激活的Activity组件接下来是否是可见的。如果是可见的,那么变量willBeVisible的值就会等于true。Activity类的成员变量mStartedActivity用来描述一个Activity组件是否正在启动一个新的Activity组件,并且等待这个新的Activity组件的执行结果。如果是的话,那么这个Activity组件的成员变量mStartedActivity的值就会等于true,表示在新的Activity组件的执行结果返回来之前,当前Activity组件要保持不可见的状态。因此,当Activity组件a的成员变量mStartedActivity的值等于true的时候,它接下来就是不可见的,否则的话,就是可见的。

       虽然说在Activity组件a的成员变量mStartedActivity的值等于true的情况下,它接下来的状态要保持不可见的,但是有可能它所启动的Activity组件的UI不是全屏的。在这种情况下,Activity组件a的UI仍然是有部分可见的,这时候也要将变量willBeVisible的值设置为true。因此,如果前面得到变量willBeVisible的值等于false,那么ActivityThread类的成员函数handleResumeActivity接下来就会通过Binder进程间通信机制来调用ActivityManagerService服务的成员函数willActivityBeVisible来检查位于Activity组件a上面的其它Activity组件(包含了Activity组件a正在等待其执行结果的Activity组件)是否是全屏的。如果不是,那么ActivityManagerService服务的成员函数willActivityBeVisible的返回值就会等于true,表示接下来需要显示Activity组件a。

      前面得到的ActivityClientRecord对象r的成员变量window用来描述当前正在激活的Activity组件a所关联的应用程序窗口对象。当它的值等于null的时候,就表示当前正在激活的Activity组件a所关联的应用程序窗口对象还没有关联一个ViewRoot对象。进一步地,如果这个正在激活的Activity组件a还活着,并且接下来是可见的,即ActivityClientRecord对象r的成员变量mFinished的值等于false,并且前面得到的变量willBeVisible的值等于true,那么这时候就说明需要为与Activity组件a所关联的一个应用程序窗口视图对象关联的一个ViewRoot对象。

      由于我们现在要给Activity组件a的应用程序窗口视图对象关联一个ViewRoot对象,因此,我们就需要首先获得这个应用程序窗口视图对象。从前面的Step 6可以知道,一个Activity组件的应用程序窗口视图对象保存在与其所关联的一个应用程序窗口对象的内部,因此,我们又要首先获得这个应用程序窗口对象。与一个Activity组件所关联的应用程序窗口对象可以通过调用该Activity组件的成员函数getWindow来获得。一旦获得了这个应用程序窗口对象(类型为PhoneWindow)之后,我们就可以调用它的成员函数getDecorView来获得它内部的视图对象。在接下来的Step 8和Step 9中,我们再分别分析Activity类的成员函数Activity类的成员函数getWindow和PhoneWindow类的成员函数getDecorView的实现。

     在关联应用程序窗口视图对象和ViewRoot对象的时候,还需要第三个参数,即应用程序窗口的布局参数,这是一个类型为WindowManager.LayoutParams的对象,可以通过调用应用程序窗口的成员函数getAttributes来获得。一切准备就绪之后,还要判断最后一个条件是否成立,即当前正在激活的Activity组件a在本地进程中是否是可见的,即它的成员变量mVisibleFromClient的值是否等于true。如果是可见的,那么最后就可以调用前面所获得的一个本地窗口管理器wm(类型为LocalWindowManager)的成员函数addView来执行关联应用程序窗口视图对象和ViewRoot对象的操作。

    接下来,我们就分别分析Activity类的成员函数getWindow、PhoneWindow类的成员函数getDecorView、ctivity类的成员函数getWindowManager以及LocalWindowManager类的成员函数addView的实现。

    Step 8. Activity.getWindow

       这一步执完成之后,返回到前面的Step 7中,即ActivityThread类的成员函数handleResumeActivity中,接下来就会继续调用前面所获得的一个PhoneWindow对象的成员函数getDecorView来获得当前正在激活的Activity组件所关联的一个应用程序窗口视图对象。

       Step 9. PhoneWindow.getDecorView

<code>    </code><code>public</code> <code>final</code> <code>View getDecorView() {</code>

<code>        </code><code>return</code> <code>mDecor;</code>

       PhoneWindow类的成员函数getDecorView首先判断成员变量mDecor的值是否等于null。如果是的话,那么就说明当前正在处理的应用程序窗口还没有创建视图对象。这时候就会调用另外一个成员函数installDecor来创建这个视图对象。从前面的调用过程可以知道,当前正在处理的应用程序窗口已经创建过视图对象,因此,这里的成员变量mDecor的值不等于null,PhoneWindow类的成员函数getDecorView直接将它返回给调用者。

       这一步执完成之后,返回到前面的Step 7中,即ActivityThread类的成员函数handleResumeActivity中,接下来就会继续调用当前正在激活的Activity组件的成员函数getWindowManager来获得一个本地窗口管理器。

       Step 10. Activity.getWindowManager

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

<code>    </code><code>public</code> <code>WindowManager getWindowManager() {</code>

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

       这一步执完成之后,返回到前面的Step 7中,即ActivityThread类的成员函数handleResumeActivity中,接下来就会继续调用前面所获得的一个LocalWindowManager对象的成员函数addView来为当前正在激活的Activity组件的应用程序窗口视图对象关联一个ViewRoot对象。

       Step 11. LocalWindowManager.addView

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

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

<code>        </code><code>public</code> <code>final</code> <code>void</code> <code>addView(View view, ViewGroup.LayoutParams params) {</code>

<code>            </code><code>mWindowManager.addView(view, params);</code>

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

<code>               </code> 

       这个函数定义在文件frameworks/base/core/java/android/view/Window.java中。

       Step 12. WindowManagerImpl.addView

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

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

<code>    </code><code>public</code> <code>void</code> <code>addView(View view, ViewGroup.LayoutParams params)</code>

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

<code>        </code><code>addView(view, params, </code><code>false</code><code>);</code>

<code>    </code><code>private</code> <code>void</code> <code>addView(View view, ViewGroup.LayoutParams params, </code><code>boolean</code> <code>nest)</code>

<code>        </code><code>final</code> <code>WindowManager.LayoutParams wparams</code>

<code>                </code><code>= (WindowManager.LayoutParams)params;</code>

<code>        </code><code>ViewRoot root;</code>

<code>        </code><code>View panelParentView = </code><code>null</code><code>;</code>

<code>        </code><code>synchronized</code> <code>(</code><code>this</code><code>) {</code>

<code>            </code><code>// Here's an odd/questionable case: if someone tries to add a</code>

<code>            </code><code>// view multiple times, then we simply bump up a nesting count</code>

<code>            </code><code>// and they need to remove the view the corresponding number of</code>

<code>            </code><code>// times to have it actually removed from the window manager.</code>

<code>            </code><code>// This is useful specifically for the notification manager,</code>

<code>            </code><code>// which can continually add/remove the same view as a</code>

<code>            </code><code>// notification gets updated.</code>

<code>            </code><code>int</code> <code>index = findViewLocked(view, </code><code>false</code><code>);</code>

<code>            </code><code>if</code> <code>(index &gt;= </code><code>0</code><code>) {</code>

<code>                </code><code>if</code> <code>(!nest) {</code>

<code>                    </code><code>throw</code> <code>new</code> <code>IllegalStateException(</code><code>"View "</code> <code>+ view</code>

<code>                            </code><code>+ </code><code>" has already been added to the window manager."</code><code>);</code>

<code>                </code><code>root = mRoots[index];</code>

<code>                </code><code>root.mAddNesting++;</code>

<code>                </code><code>// Update layout parameters.</code>

<code>                </code><code>view.setLayoutParams(wparams);</code>

<code>                </code><code>root.setLayoutParams(wparams, </code><code>true</code><code>);</code>

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

<code>            </code><code>// If this is a panel window, then find the window it is being</code>

<code>            </code><code>// attached to for future reference.</code>

<code>            </code><code>if</code> <code>(wparams.type &gt;= WindowManager.LayoutParams.FIRST_SUB_WINDOW &amp;&amp;</code>

<code>                    </code><code>wparams.type &lt;= WindowManager.LayoutParams.LAST_SUB_WINDOW) {</code>

<code>                </code><code>final</code> <code>int</code> <code>count = mViews != </code><code>null</code> <code>? mViews.length : </code><code>0</code><code>;</code>

<code>                </code><code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i&lt;count; i++) {</code>

<code>                    </code><code>if</code> <code>(mRoots[i].mWindow.asBinder() == wparams.token) {</code>

<code>                        </code><code>panelParentView = mViews[i];</code>

<code>            </code><code>root = </code><code>new</code> <code>ViewRoot(view.getContext());</code>

<code>            </code><code>root.mAddNesting = </code><code>1</code><code>;</code>

<code>            </code><code>view.setLayoutParams(wparams);</code>

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

<code>                </code><code>index = </code><code>1</code><code>;</code>

<code>                </code><code>mViews = </code><code>new</code> <code>View[</code><code>1</code><code>];</code>

<code>                </code><code>mRoots = </code><code>new</code> <code>ViewRoot[</code><code>1</code><code>];</code>

<code>                </code><code>mParams = </code><code>new</code> <code>WindowManager.LayoutParams[</code><code>1</code><code>];</code>

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

<code>                </code><code>index = mViews.length + </code><code>1</code><code>;</code>

<code>                </code><code>Object[] old = mViews;</code>

<code>                </code><code>mViews = </code><code>new</code> <code>View[index];</code>

<code>                </code><code>System.arraycopy(old, </code><code>0</code><code>, mViews, </code><code>0</code><code>, index-</code><code>1</code><code>);</code>

<code>                </code><code>old = mRoots;</code>

<code>                </code><code>mRoots = </code><code>new</code> <code>ViewRoot[index];</code>

<code>                </code><code>System.arraycopy(old, </code><code>0</code><code>, mRoots, </code><code>0</code><code>, index-</code><code>1</code><code>);</code>

<code>                </code><code>old = mParams;</code>

<code>                </code><code>mParams = </code><code>new</code> <code>WindowManager.LayoutParams[index];</code>

<code>                </code><code>System.arraycopy(old, </code><code>0</code><code>, mParams, </code><code>0</code><code>, index-</code><code>1</code><code>);</code>

<code>            </code><code>index--;</code>

<code>            </code><code>mViews[index] = view;</code>

<code>            </code><code>mRoots[index] = root;</code>

<code>            </code><code>mParams[index] = wparams;</code>

<code>        </code><code>// do this last because it fires off messages to start doing things</code>

<code>        </code><code>root.setView(view, wparams, panelParentView);</code>

<code>    </code><code>private</code> <code>View[] mViews;</code>

<code>    </code><code>private</code> <code>ViewRoot[] mRoots;</code>

<code>    </code><code>private</code> <code>WindowManager.LayoutParams[] mParams;</code>

      这个函数定义在文件frameworks/base/core/java/android/view/WindowManagerImpl.java中。

      在WindowManagerImpl类中,两个参数版本的成员函数addView是通过调用三个参数版本的成同函数addView来实现的,因此,我们接下来就主要分析三个参数版本的成员函数addView的实现。

      在分析WindowManagerImpl类的三个参数版本的成员函数addView的实现之前,我们首先了解一下WindowManagerImpl类是如何关联一个应用程序窗口视图对象(View对象)和一个ViewRoot对象的。一个View对象在与一个ViewRoot对象关联的同时,还会关联一个WindowManager.LayoutParams对象,这个WindowManager.LayoutParams对象是用来描述应用程序窗口视图的布局属性的。

      WindowManagerImpl类有三个成员变量mViews、mRoots和mParams,它们分别是类型为View、ViewRoot和WindowManager.LayoutParams的数组。这三个数组的大小是始终保持相等的。这样, 有关联关系的View对象、ViewRoot对象和WindowManager.LayoutParams对象就会分别保存在数组mViews、mRoots和mParams的相同位置上,也就是说,mViews[i]、mRoots[i]和mParams[i]所描述的View对象、ViewRoot对象和WindowManager.LayoutParams对象是具有关联关系的。因此,WindowManagerImpl类的三个参数版本的成员函数addView在关联一个View对象、一个ViewRoot对象和一个WindowManager.LayoutParams对象的时候,只要分别将它们放在数组mViews、mRoots和mParams的相同位置上就可以了。

      理解了一个View对象、一个ViewRoot对象和一个WindowManager.LayoutParams对象是如何关联之后,WindowManagerImpl类的三个参数版本的成员函数addView的实现就容易理解了。

      参数view和参数params描述的就是要关联的View对象和WindowManager.LayoutParams对象。成员函数addView首先调用另外一个成员函数findViewLocked来检查参数view所描述的一个View对象是否已经存在于数组中mViews中了。如果已经存在的话,那么就说明该View对象已经关联过ViewRoot对象以及WindowManager.LayoutParams对象了。在这种情况下,如果参数nest的值等于false,那么成员函数addView是不允许重复对参数view所描述的一个View对象进行重新关联的。另一方面,如果参数nest的值等于true,那么成员函数addView只是重新修改参数view所描述的一个View对象及其所关联的一个ViewRoot对象内部使用的一个WindowManager.LayoutParams对象,即更新为参数params所描述的一个WindowManager.LayoutParams对象,这是通过调用它们的成员函数setLayoutParams来实现的。

      如果参数view所描述的一个View对象还没有被关联过一个ViewRoot对象,那么成员函数addView就会创建一个ViewRoot对象,并且将它与参数view和params分别描述的一个View对象和一个WindowManager.LayoutParams对象保存在数组mViews、mRoots和mParams的相同位置上。注意,如果数组mViews、mRoots和mParams尚未创建,那么成员函数addView就会首先分别为它们创建一个大小为1的数组,以便可以用来分别保存所要关联的View对象、ViewRoot对象和WindowManager.LayoutParams对象。另一方面,如果数组mViews、mRoots和mParams已经创建,那么成员函数addView就需要分别将它们的大小增加1,以便可以在它们的末尾位置上分别保存所要关联的View对象、ViewRoot对象和WindowManager.LayoutParams对象。

      还有另外一个需要注意的地方是当参数view描述的是一个子应用程序窗口的视图对象时,即WindowManager.LayoutParams对象wparams的成员变量type的值大于等于WindowManager.LayoutParams.FIRST_SUB_WINDOW并且小于等于WindowManager.LayoutParams.LAST_SUB_WINDOW时,那么成员函数addView还需要找到这个子视图对象的父视图对象panelParentView,这是通过遍历数组mRoots来查找的。首先,WindowManager.LayoutParams对象wparams的成员变量token指向了一个类型为W的Binder本地对象的一个IBinder接口,用来描述参数view所描述的一个子应用程序窗口视图对象所属的父应用程序窗口视图对象。其次,每一个ViewRoot对象都通过其成员变量mWindow来保存一个类型为W的Binder本地对象,因此,如果在数组mRoots中,存在一个ViewRoot对象,它的成员变量mWindow所描述的一个W对象的一个IBinder接口等于WindowManager.LayoutParams对象wparams的成员变量token所描述的一个IBinder接口时,那么就说明与该ViewRoot对象所关联的View对象即为参数view的父应用程序窗口视图对象。

       成员函数addView为参数view所描述的一个View对象和参数params所描述的一个WindowManager.LayoutParams对象关联好一个ViewRoot对象root之后,最后还会将这个View对view象和这个WindowManager.LayoutParams对象,以及变量panelParentView所描述的一个父应用程序窗视图对象,保存在这个ViewRoot对象root的内部去,这是通过调用这个ViewRoot对象root的成员函数setView来实现的,因此,接下来我们就继续分析ViewRoot类的成员函数setView的实现。

       Step 13. ViewRoot.setView

<code>public</code> <code>final</code> <code>class</code> <code>ViewRoot </code><code>extends</code> <code>Handler </code><code>implements</code> <code>ViewParent,</code>

<code>        </code><code>View.AttachInfo.Callbacks {</code>

<code>    </code><code>final</code> <code>WindowManager.LayoutParams mWindowAttributes = </code><code>new</code> <code>WindowManager.LayoutParams();</code>

<code>    </code><code>View mView;</code>

<code>    </code><code>final</code> <code>View.AttachInfo mAttachInfo;</code>

<code>    </code><code>boolean</code> <code>mAdded;</code>

<code>    </code><code>public</code> <code>void</code> <code>setView(View view, WindowManager.LayoutParams attrs,</code>

<code>            </code><code>View panelParentView) {</code>

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

<code>                </code><code>mView = view;</code>

<code>                </code><code>mWindowAttributes.copyFrom(attrs);</code>

<code>                </code><code>mAttachInfo.mRootView = view;</code>

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

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

<code>                    </code><code>mAttachInfo.mPanelParentWindowToken</code>

<code>                            </code><code>= panelParentView.getApplicationWindowToken();</code>

<code>                </code><code>mAdded = </code><code>true</code><code>;</code>

<code>                </code><code>requestLayout();</code>

<code>                    </code><code>res = sWindowSession.add(mWindow, mWindowAttributes,</code>

<code>                            </code><code>getHostVisibility(), mAttachInfo.mContentInsets,</code>

<code>                            </code><code>mInputChannel);</code>

<code>                    </code><code>mAdded = </code><code>false</code><code>;</code>

<code>                    </code><code>mView = </code><code>null</code><code>;</code>

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

<code>                    </code><code>throw</code> <code>new</code> <code>RuntimeException(</code><code>"Adding window failed"</code><code>, e);</code>

<code>                </code><code>} </code><code>finally</code> <code>{</code>

<code>                    </code><code>if</code> <code>(restore) {</code>

<code>                        </code><code>attrs.restore();</code>

       这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。

       参数view所描述的一个View对象会分别被保存在ViewRoot类的成员变量mView以及成员变量mAttachInfo所描述的一个AttachInfo的成员变量mRootView中,而参数attrs所描述的一个WindowManager.LayoutParams对象的内容会被拷贝到ViewRoot类的成员变量mWindowAttributes中去。

      当参数panelParentView的值不等于null的时候,就表示参数view描述的是一个子应用程序窗口视图对象。在这种情况下,参数panelParentView描述的就是一个父应用程序窗口视图对象。这时候我们就需要获得用来描述这个父应用程序窗口视图对象的一个类型为W的Binder本地对象的IBinder接口,以便可以保存在ViewRoot类的成员变量mAttachInfo所描述的一个AttachInfo的成员变量mPanelParentWindowToken中去。这样以后就可以知道ViewRoot类的成员变量mView所描述的一个子应用程序窗口视图所属的父应用程序窗口视图是什么了。注意,通过调用参数panelParentView的所描述的一个View对象的成员函数getApplicationWindowToken即可以获得一个对应的W对象的IBinder接口。

      上述操作执行完成之后,ViewRoot类的成员函数setView就可以将成员变量mAdded的值设置为true了,表示当前正在处理的一个ViewRoot对象已经关联好一个View对象了。接下来,ViewRoot类的成员函数setView还需要执行两个操作:

      1. 调用ViewRoot类的另外一个成员函数requestLayout来请求对应用程序窗口视图的UI作第一次布局。

      2. 调用ViewRoot类的静态成员变量sWindowSession所描述的一个类型为Session的Binder代理对象的成员函数add来请求WindowManagerService增加一个WindowState对象,以便可以用来描述当前正在处理的一个ViewRoot所关联的一个应用程序窗口。

      至此,我们就分析完成Android应用程序窗口视图对象的创建过程了。在接下来的一篇文章中,我们将会继续分析Android应用程序窗口与WindowManagerService服务的连接过程,即Android应用程序窗口请求WindowManagerService为其增加一个WindowState对象的过程,而在接下来的两篇文章中,我们还会分析用来渲染Android应用程序窗口的Surface的创建过程,以及Android应用程序窗口的渲染过程。通过这三个过程的分析,我们就可以看到上述第1点和第2点提到的两个函数的执行过程,敬请期待!

本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/1242881,如需转载请自行联系原作者

继续阅读