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

从Android应用程序启动过程中可以知道,Activity组件在启动的过程中,会调用ActivityThread类的成员函数handleLaunchActivity,来创建Activity,那么我们就来分析应用程序窗口的视图对象的创建过程。

开始本篇文章之前,如果对Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析不太清楚的可以点这个链接观看。

本篇文章主要分为这几个点:

  • ActivityThread.handleLaunchActivity
  • ActivityThread.performLaunchActivity
  • Activity.setContentView
  • PhoneWindow.setContentView
  • PhoneWindow.installDecor
  • ActivityThread.handleResumeActivity
  • Activity.getWindowManager
  • WindowManagerImpl.addView
  • WindowManagerGlobal.addView

1. ActivityThread.handleLaunchActivity

public final class ActivityThread {  
    ......  
    private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
        ......  
        Activity a = performLaunchActivity(r, customIntent);  
        if (a != null) {  
            ......  
            handleResumeActivity(r.token, false, r.isForward);  
           ......  
        }  
      ......  
    }  
  ......  
}  

在这个函数中会调用performLaunchActivity来创建要启动的Activity组件,同时还会创建还会为该Activity组件创建窗口对象和视图对象。
然后通过调用ActivityThread类的成员函数handleResumeActivity来激活Activity。
接下来我们就一一分析

2. ActivityThread.performLaunchActivity

这个方法只要是创建一个Activity实例,然后调用Activity中的onCreate来进行一些初始化操作,而onCreate方法一般都是我们自己重写了,其中会在方法中设置布局文件setContentView(R.layout.main); 接下来就看下这个方法。

3. Activity.setContentView

public Window getWindow() {
        return mWindow;
    }

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
       ......
    }

我们知道通过getWindow获得了Window对象,然后再调用这个窗口对象的setContentView,来实现创建窗口视图。
从Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析这片文章中我们可以知道这个mWindow具体是PhoneWindow,那么接下来就来看看PhoneWindow的setContentView方法。

4. PhoneWindow.setContentView

  public void setContentView(int layoutResID) {
  
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
          ......
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    ......
    }

PhoneWindow类的成员变量mContentParent是一个ViewGroup类型的成员变量,用作UI容器。当这个值为null的时候,说明这个这个窗口的视图还没有创建,那么就会调用installDecor();来创建窗口的视图对象。否则的话就要重新设置窗口的视图,在设置之前,会调用mContentParent.removeAllViews来移除原来的UI视图。
一开始创建的时候mContentParent 我们假设它为空,那么就会调用installDecor来创建应用程序窗口视图对象,紧接着会调用 mLayoutInflater.inflate来把参数为layoutResID的布局文件设置到窗口视图中,最后调用Callback接口的成员函数onContentChanged来通知对应的Activity组件,视图内容发生改变了。Activity组件自己实现了这个Callback接口,因此,前面实际调用的是Activity类的成员函数onContentChanged来发出一个视图内容变化通知。

5. PhoneWindow.installDecor

  private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            ......
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
             ......
                mTitleView = findViewById(R.id.title);
                if (mTitleView != null) {
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        final View titleContainer = findViewById(R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        mContentParent.setForeground(null);
                    } else {
                        mTitleView.setText(mTitle);
                    }
                }
            }
    }

首先进来会判断类型为DecorView的PhoneWindow类的成员变量mDecor的值是否等于null,如果为空那就调用成员函数generateLayout来创建一个DecorView对象,并用mDecor来保存。DecorView是一个FrameLayout,主要内容包含标题栏和内容栏,这个时候DecorView还是一个空白的FrameLayout
紧接着会调用另外一个成员函数generateLayout来加载当前应用程序窗口对应的布局,然后返回一个窗口UI容器,保存到成员变量mContentParent 中。
之后还会通过id来获取id为title的TexView,判断这个空间需不需要进行显示。
这一步执行完成之后,应用程序窗口视图就创建完成了,那如何让视图显示出来的那?我们接着看。

6. ActivityThread.handleResumeActivity

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {

        ActivityClientRecord r = mActivities.get(token);
         .....
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;
          ......
            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManager.getService().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
              
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);
                    } 
                }
         ......
            } 
      ......
    }

首先那会调用performResumeActivity来通知Activity要被激活了,然后返回来一个值为类型为ActivityClientRecord的 r 对象,其中r.activity返回的就是被激活的Activity 用a来表示。 那么就会使Activity组件调用成员函数onResume 。

接下来会判断当前正在激活的Activity组件接下来是否可见,这里有两个需要先弄明白的,一个是boolen 类型的willBeVisible ,true表示正在激活的Activity组件接下来是可见的,反之不可见,另一个是a.mStartedActivity获得的boolean类型的值,true来表示一个Activity组件正在启动一个新的Activity,并且等在这个新的Activity执行结束。返回结果之前,当前的Activity组件要保持不可见状态。总结一句话就是当Activity组件a的成员变量mStartedActivity的值等于true的时候,它接下来就是不可见的,否则的话,就是可见的。

但是有一种情况就是所启动的Activity的UI不是全屏的,但是mStartedActivity为true,那么原来的Activity的UI一部分是可见的,这样的话willBeVisible 必须为true才行,如果之前的willBeVisible为false那么接下来会通过ActivityManager.getService().willActivityBeVisible(
a.getActivityToken());来检查新建的Activity是否是全屏,按照结果来重新给willBeVisible 赋值。

接下来判断ActivityClientRecord 的r的成员变量window是否为空,如果为空则表示当前正在激活的Activity组件a关联的Window(应用程序窗口)还没有关联ViewRootImpl对象,如果正在激活的Activity a还活着,a.mFinished为false,并且可见willBeVisible为true,这时候就需要为a创建一个ViewRootImpl对象,那么就需要接下来的几步了。通过r.activity.getWindow()获得PhoneWindow,通过 r.window.getDecorView(),获得DecorView,(不懂的可以看这一篇Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析

7. Activity.getWindowManager

public class Activity extends ContextThemeWrapper  
        implements LayoutInflater.Factory,  
        Window.Callback, KeyEvent.Callback,  
        OnCreateContextMenuListener, ComponentCallbacks {  
    ......  
  
    private WindowManager mWindowManager;  
    ......  
  
    public WindowManager getWindowManager() {  
        return mWindowManager;  
    }  
  
    ......  
}  

这里的mWindowManager指向的是WindowManagerImpl。

8.WindowManagerImpl.addView

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); 

 public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

这里调用的是类型为WindowManagerGlobal 的mGlobal的成员函数addView,WindowManagerGlobal相当于一个代理来管理窗口。

9.WindowManagerGlobal.addView

  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
       ......
   final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        ViewRootImpl root;
      ......

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

在这个方法中我们可以看到创建了一个ViewRootImpl类型的root来实现与从窗口的关联。最后调用 root.setView(view, wparams, panelParentView);来实现。参数view和参数mparams描述的就是要关联的View对象和WindowManager.LayoutParams对象。

至此,我们就分析完成Android应用程序窗口视图对象的创建过程了

你可能感兴趣的:(Android应用程序窗口(Activity)的视图对象(View)的创建过程分析)