Activity的创建,配置上下文,绘制视图过程

来源:互联网 发布:动态寻路算法 编辑:程序博客网 时间:2024/05/29 07:50

概述

在Activity启动过程分析一文中分析到了通过反射创建了activity并且调用了activity的onCreate表示activity启动了,但是我们见到的activity一般都是一个界面,所以这篇文章就继续分析了Activity的视图创建过程.这个过程中涉及到的主要类如下:
Activity->用户操作的类
Context-> 负责管理actvity的资源等信息
Window-> 真正表示界面的类,也就是窗口,管理要显示内容,用来描述当前正在启动的应用程序窗口的。这个应用程序窗口在运行的过程中,会接收到一些事件,例如,键盘、触摸屏事件等,这些事件需要转发给与它所关联的Activity组件处理,这个转发操作是通过一个Window.Callback接口来实现的
ViewRootImpl -> 与WMS通信使用mSseionWindow,并且将消息发送到主线程,保证ui的顺序创建,且检测UI的更新必须在主线程
WindowManagerService ->负责所有界面的管理,每个activity队友一个WindowState对象在这里,
SurfaceFlinger -> 负责真正的绘制界面

每一个Activity组件都有对应一个window,一个对应的ViewRootImpl对象、View对象以及WindowManager.LayoutParams对象,这三个对象的对应关系是由WindowManagerImpl类来维护的。具体来说,就是由WindowManagerImpl类的成员变量mRoots、mViews和mParams所描述的三个数组来维护的.

每一个ViewRootImpl对象都有一个类型为Surface的成员变量mSurface,它指向了一个Java层的Surface对象。这个Java层的Surface对象通过它的成员变量mNativeSurface与一个C++层的Surface对象。C++层中,每一个Surface对象都有一个对应的SurfaceControl对象。这个对应的SurfaceControl对象是用来设置应用程序窗口的属性,例如,设置大小、位置等属性。

每一个Activity组件在WindowManagerService服务这一侧都有一个对应的WindowState对象,用来描述Activity组件的窗口状态。WindowState类有两个重要的成员变量mSession和mSurface,它们的类型分别为SurfaceSession和Surface。SurfaceSession类有一个类型为int的成员变量mClient,它保存的是一个C++层的SurfaceComposerClient对象的地址,即每一个Java层的SurfaceSession对象在C++层都有一个对应的SurfaceComposerClient对象。当一个SurfaceSession对象创建的时候,与它所关联的SurfaceComposerClient对象也会同时被创建。每一个Android应用程序进程在WindowManagerService服务这一侧对应有一个Session对象、一个SurfaceSession对象以及一个SurfaceComposerClient对象。由于每一个Android应用程序进程都可以运行若干个Activity组件,因此,我们又可以说,Activity组件与WindowServiceManager服务这一侧的Session对象、SurfaceSession对象以及SurfaceComposerClient对象是多对一的关系。因此,关系如下:
在Wms中通过WindowState来操作每一个activity,在activity通过mSessionWindow来操作Wms.

Activity的创建,启动过程

1.创建

   public Activity newActivity(ClassLoader cl, String className,            Intent intent)            throws InstantiationException, IllegalAccessException,            ClassNotFoundException {        return (Activity)cl.loadClass(className).newInstance();    }

通过类加载器将对应的类加载进内存,然后通过反射创建一个Activity对象

2.Activty的初始化: 配置上下文环境,资源,创建窗口等

  1.    2.     final void attach(Context context, ActivityThread aThread,    3.             Instrumentation instr, IBinder token, int ident,    4.             Application application, Intent intent, ActivityInfo info,    5.             CharSequence title, Activity parent, String id,    6.             Object lastNonConfigurationInstance,    7.             HashMap<String,Object> lastNonConfigurationChildInstances,    8.             Configuration config) {    9.          //将conextt设置ContextThemeWrapper包装类  10.         attachBaseContext(context);    11.         //创建window  12.         mWindow = PolicyManager.makeNewWindow(this);    13.       设置activity为window的回调,这样就可以将在activity处理逻辑事件  14.         mWindow.setCallback(this);    15.   16.   17. 参数info指向的是一个ActivityInfo对象,用来描述当前正在启动的Activity组件的信息。其中,这个ActivityInfo对象的成员变量softInputMode用来描述当前正在启动的一个Activity组件是否接受软键盘输入。如果接受的话,那么它的值就不等于WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,并且描述的是当前正在启动的Activity组件所接受的软键盘输入模式。这个软键盘输入模式设置到前面所创建的一个PhoneWindow对象内部去,这是通过调用Window类的成员函数setSoftInputMode来实现的。  18.         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {    19.             mWindow.setSoftInputMode(info.softInputMode);    20.         }    21.         ......    22.     23.         mApplication = application;    24.         ......    25.          activity中的   窗口管理者和window中的是同一个mToken,它是一个Binder代理对象,引用了在ActivityManagerService这一侧所创建的一个类型为ActivityRecord的Binder本地对象,用来描述该Activity组件的运行状态。这个Binder代理对象会被保存在Window类的成员变量mAppToken中,这样当前正在处理的窗口就可以知道与它所关联的Activity组件是什么。  26.         mWindow.setWindowManager(null, mToken, mComponent.flattenToString());    27.         mWindowManager = mWindow.getWindowManager();    28.             配置信息  29.         mCurrentConfig = config;    30.     }  

初始化一个Activity组件实例需要一个Application对象app、一个ContextImpl对象appContext以及一个Configuration对象config,它们分别用来描述该Activity组件实例的应用程序信息、运行上下文环境以及配置信息。一个activity的资源都在ContextImpl中管理,所以在初始activity只要设好对应的Context,在启动activity的时候 ,用到资源才不会报错,插件化的时候要替换资源其实就是替换context

3.Window的创建

  1. public class PhoneWindow extends Window implements MenuBuilder.Callback {    2.     ......    3.     4.     // This is the top-level view of the window, containing the window decor.    5.     private DecorView mDecor;    6.     7.     // This is the view in which the window contents are placed. It is either    8.     // mDecor itself, or a child of mDecor where the contents go.    9.     private ViewGroup mContentParent;    10.     ......    11.     12.     private LayoutInflater mLayoutInflater;    13.     ......    14.     15.     public PhoneWindow(Context context) {    16.         super(context);    17.         mLayoutInflater = LayoutInflater.from(context);    18.     }    19.     20.     ......    21. } 

一个Activity组件所关联的应用程序窗口对象的类型为PhoneWindow。
这个类型为PhoneWindow的应用程序窗口是通过一个类型为LocalWindowManager的本地窗口管理器来维护的。
这个类型为LocalWindowManager的本地窗口管理器又是通过一个类型为WindowManagerImpl的窗口管理器来维护应用程序窗口的。
这个类型为PhoneWindow的应用程序窗口内部有一个类型为DecorView的视图对象,这个视图对象才是真正用来描述一个Activity组件的UI的。

4.Activty的启动

调用mInstrumentation就会调用Activity的OnCreate方法mInstrumentation.callActivityOnCreate(activity, r.state);Activity默认的onCreate方法  1.  protected void onCreate(Bundle savedInstanceState) {    2.         mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(    3.                 com.android.internal.R.styleable.Window_windowNoDisplay, false);    4.         mCalled = true;    5.     }

一般来说,我们都是通过定义一个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的窗口视图的创建

1.从oncreate中调用setContentView开始,就是创建窗口视图的开始

  1. public void setContentView(int layoutResID) {    2.         getWindow().setContentView(layoutResID);    3.     }  

2.PhoneWindow.setContentView

  1. public class PhoneWindow extends Window implements MenuBuilder.Callback {    2.       3.    window的顶级视图,但是它也是Framelayout的子类,这个是出去状态栏和虚拟按键的部分,调用这个的getTop就可以知道状态栏的高度了  4.    private DecorView mDecor;    5.   6.     id等于content的布局,其实就是我们自定义好的布局的父布局,mContentParent是mDecor的间接子类,直接子类是系统定义的一些包含id值为“content”布局,根据用户的feature,加载不同的布局,这个是除去标题栏的部分,这个调用getTop就可以知道标题栏的高度了  7.     private ViewGroup mContentParent;    8.     ......    9.     10.     @Override    11.     public void setContentView(int layoutResID) {    12.         mContentParent 等于null的时候,就说明正在处理的应用程序窗口的视图对象还没有创建  13.         if (mContentParent == null) {    14.          创建窗口视图,就是创建mDecor 和mContentParent对象,这里会根据feature,让mDecor来addview对应的布局  15.             installDecor();    16.         } else {    17.        否则就是表示要重新设置视图,先清空  18.             mContentParent.removeAllViews();    19.         }    20.        将他们的布局添加到mContentParent中  21.         mLayoutInflater.inflate(layoutResID, mContentParent);    22.         final Callback cb = getCallback();    23.         if (cb != null) {    24.             调用Callback 的回调onContentChanged,其实就调用activity的onContentChanged  25.             cb.onContentChanged();    26.         }    27.     }    28.     29.       30. } 

3.PhoneWindow.installDecor

  1. private void installDecor() {    2.         if (mDecor == null) {    3.           创建mDecor  4.             mDecor = generateDecor();    5.             ......    6.         }    7.         根据mDecor创建自定义布局的父布局  8.         if (mContentParent == null) {    9.             mContentParent = generateLayout(mDecor);    10.         11.        根据我们设置的feature来设置标题  12.             mTitleView = (TextView)findViewById(com.android.internal.R.id.title);    13.             if (mTitleView != null) {    14.                 if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {    15.                     View titleContainer = findViewById(com.android.internal.R.id.title_container);    16.                     if (titleContainer != null) {    17.                         titleContainer.setVisibility(View.GONE);    18.                     } else {    19.                         mTitleView.setVisibility(View.GONE);    20.                     }    21.                     if (mContentParent instanceof FrameLayout) {    22.                         ((FrameLayout)mContentParent).setForeground(null);    23.                     }    24.                 } else {    25.                     mTitleView.setText(mTitle);    26.                 }    27.             }    28.         }    29.     }    30.     31.     ......    32. } 

4.PhoneWindow.generateDecor

 protected DecorView generateDecor() {        return new DecorView(getContext(), -1);    }

generateDcor其实就是new一个DecorView对象而已,而DecorView其实就是Framelayout的子类,DecorView的定义如下:
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {}
mDecor创建好了在看下是如何来创建mContentParent的

5.PhoneWindow.generateLayout

protected ViewGroup generateLayout(DecorView decor) {        // Apply data from current theme.      通过主题设置的属性,然后调用requestFeature来给窗口设置对应的属性,在requestFeature中会先判断mContentParent是否为空,如果不为空,就会抛出异常,"requestFeature() must be called before adding content,这就是我们在代码中设置Feature时在要setContentView之前的原因        TypedArray a = getWindowStyle();        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {            // Don't allow an action bar if there is no title.            requestFeature(FEATURE_ACTION_BAR);        }        if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {            requestFeature(FEATURE_ACTION_BAR_OVERLAY);        }        if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {            requestFeature(FEATURE_ACTION_MODE_OVERLAY);        }        if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {            requestFeature(FEATURE_SWIPE_TO_DISMISS);        }        if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));        }        if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,                false)) {            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS                    & (~getForcedWindowFlags()));        }        if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,                false)) {            setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION                    & (~getForcedWindowFlags()));        }        if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));        }        if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));        }        if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,                getContext().getApplicationInfo().targetSdkVersion                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));        }        a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);        a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);        if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();            a.getValue(R.styleable.Window_windowFixedWidthMajor,                    mFixedWidthMajor);        }        if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();            a.getValue(R.styleable.Window_windowFixedWidthMinor,                    mFixedWidthMinor);        }        if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();            a.getValue(R.styleable.Window_windowFixedHeightMajor,                    mFixedHeightMajor);        }        if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();            a.getValue(R.styleable.Window_windowFixedHeightMinor,                    mFixedHeightMinor);        }        if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {            requestFeature(FEATURE_CONTENT_TRANSITIONS);        }        if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {            requestFeature(FEATURE_ACTIVITY_TRANSITIONS);        }        final Context context = getContext();        final int targetSdk = context.getApplicationInfo().targetSdkVersion;        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;        final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;        final boolean targetHcNeedsOptions = context.getResources().getBoolean(                R.bool.target_honeycomb_needs_options_menu);判断是否有actionBar        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);        } else {            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);        }获取状态栏和导航栏的颜色        if (!mForcedStatusBarColor) {            mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);        }        if (!mForcedNavigationBarColor) {            mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);        }     调用setSystemUiVisibility来设置窗口的显示状态,是否显示状态栏,是否全屏等        if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {            decor.setSystemUiVisibility(                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);        }       获取Params        WindowManager.LayoutParams params = getAttributes();      设置软键盘的模式        if (!hasSoftInputMode()) {            params.softInputMode = a.getInt(                    R.styleable.Window_windowSoftInputMode,                    params.softInputMode);        }        // 根据设置的feature,加载系统定义好的含有id=content的布局,是否有标题栏,actionbar,进度条等        int layoutResource;        int features = getLocalFeatures();        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {            layoutResource = R.layout.screen_swipe_dismiss;        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        R.attr.dialogTitleIconsDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = R.layout.screen_title_icons;            }            // XXX Remove this once action bar supports these features.            removeFeature(FEATURE_ACTION_BAR);            // System.out.println("Title Icons!");        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {            // Special case for a window with only a progress bar (and title).            // XXX Need to have a no-title version of embedded windows.            layoutResource = R.layout.screen_progress;            // System.out.println("Progress!");        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {            // Special case for a window with a custom title.            // If the window is floating, we need a dialog layout            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        R.attr.dialogCustomTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = R.layout.screen_custom_title;            }            // XXX Remove this once action bar supports these features.            removeFeature(FEATURE_ACTION_BAR);        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {            // If no other features and not embedded, only need a title.            // If the window is floating, we need a dialog layout            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        R.attr.dialogTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {                layoutResource = a.getResourceId(                        R.styleable.Window_windowActionBarFullscreenDecorLayout,                        R.layout.screen_action_bar);            } else {                layoutResource = R.layout.screen_title;            }            // System.out.println("Title!");        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {            layoutResource = R.layout.screen_simple_overlay_action_mode;        } else {            // Embedded, so no decoration is needed.            layoutResource = R.layout.screen_simple;            // System.out.println("Simple!");        }        mDecor.startChanging();        View in = mLayoutInflater.inflate(layoutResource, null);通过mLayoutInflater加载好对应的布局,然后添加到decor中        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        mContentRoot = (ViewGroup) in;//这里就找到id为conteng的布局,也就是我们自己定义的布局的直接父布局了        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);        if (contentParent == null) {            throw new RuntimeException("Window couldn't find content container view");        }设置背景        if (getContainer() == null) {            final Drawable background;            if (mBackgroundResource != 0) {                background = getContext().getDrawable(mBackgroundResource);            } else {                background = mBackgroundDrawable;            }            mDecor.setWindowBackground(background);            final Drawable frame;            if (mFrameResource != 0) {                frame = getContext().getDrawable(mFrameResource);            } else {                frame = null;            }            mDecor.setWindowFrame(frame);            mDecor.setElevation(mElevation);            mDecor.setClipToOutline(mClipToOutline);            if (mTitle != null) {                setTitle(mTitle);            }            if (mTitleColor == 0) {                mTitleColor = mTextColor;            }            setTitleColor(mTitleColor);        }        mDecor.finishChanging();        return contentParent;    }

这个方法主要就是根据设置的feature加载含有id=content的布局,然后添加到decor中,然后在加载id=content的布局当contentParent.所以就是decor->含有id=content的布局(包含标题栏,等)->我们自定的布局.
拿到contengParent之后就又回到setContentView中通过mLayoutInflater加载我们自己定义的布局,在通过callback回调给activit的onContentChanged,到此应用程序窗口视图就创建完成了.下面通过一张图表示window,Decorview和mContentParent的关系
这里写图片描述

窗口视图的显示

窗口的视图的创建是在oncreate中完成的,而oncreate是在handleLaunchActivity的performLaunchActivity中创建出activity并且调用的,窗口的显示就是要调用handleLaunchActivityhandleResumeActivity(r.token, false, r.isForward);来完成了.

1.ActivityThread.handleLaunchActivity

  1. public final class ActivityThread {    2.     ......    3.     4.     private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {    5.         ......    6.        //创建activity  7.         Activity a = performLaunchActivity(r, customIntent);    8.     9.         if (a != null) {    10.             ......    11.            显示activity  12.             handleResumeActivity(r.token, false, r.isForward);    13.     14.             ......    15.         }    16.     17.         ......    18.     }    19.     20.     ......    21. } 

2.ActivityThread.handleResumeActivity

  1. public final class ActivityThread {    2.     ......    3.     4.     final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {    5.         ......    6.   通知Activity组件,它要被激活了,即会导致Activity组件的成员函数onResume被调用  7.         ActivityClientRecord r = performResumeActivity(token, clearHide);    8.     9.          r就表示要激活的activity  10.         if (r != null) {    11.             final Activity a = r.activity;    12.             ......    13.            Activity类的成员变量mStartedActivity用来描述一个Activity组件是否正在启动一个新的Activity组件,并且等待这个新的Activity组件的执行结果。如果是的话,那么这个Activity组件的成员变量mStartedActivity的值就会等于true,表示在新的Activity组件的执行结果返回来之前,当前Activity组件要保持不可见的状态.  14.             boolean willBeVisible = !a.mStartedActivity;    15.             if (!willBeVisible) {    16.                 try {    17.                     willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(    18.                             a.getActivityToken());    19.                 } catch (RemoteException e) {    20.                 }    21.             }    22. r.window用来描述当前正在激活的Activity组件a所关联的应用程序窗口对象,当它的值等于null的时候,就表示当前正在激活的Activity组件a所关联的应用程序窗口对象还没有关联一个ViewRoot对象,如果这个acitiviy是活着的且接下来是要可见的就要为它创建一个window和viewroot对象  23.             if (r.window == null && !a.mFinished && willBeVisible) {    24.                 通过activity的getWindow(拿到对应的窗口对象  25.                 r.window = r.activity.getWindow();    26.                  在拿到窗口对应的视图对象  27.                 View decor = r.window.getDecorView();    28.                 decor.setVisibility(View.INVISIBLE);    29.                 拿到窗口对应的窗口管理者  30.                 ViewManager wm = a.getWindowManager();    31.                拿到布局参数  32.                 WindowManager.LayoutParams l = r.window.getAttributes();    33.                 a.mDecor = decor;    34.                 l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;    35.                 ......    36.                 在判断正在激活的Activity组件a在本地进程中是否是可见的,即它的成员变量mVisibleFromClient的值是否等于true  37.                 if (a.mVisibleFromClient) {    38.                     a.mWindowAdded = true;    39.                    调用窗口管理者的addview方法将布局显示出来  40.                     wm.addView(decor, l);    41.                 }    42.             }     43.     44.             ......    45.         }    46.     47.         ......    48.     }    49.       50.     ......    51. }  

这里的wm 是一个LocalWindowManager 对象,但是他实际是是利用的WindowManagerImpl对象的addview方法来完成的,

3.WindowManagerImpl .addview

  1. public class WindowManagerImpl implements WindowManager {    2.     ......    3.     4.     public void addView(View view, ViewGroup.LayoutParams params)    5.     {    6.         addView(view, params, false);    7.     }    8.     9.     ......    10.     11.     private void addView(View view, ViewGroup.LayoutParams params, boolean nest)    12.     {    13.         ......    14.     15.         final WindowManager.LayoutParams wparams    16.                 = (WindowManager.LayoutParams)params;    17.     18.         ViewRootImpl root;    19.         View panelParentView = null;    20.     21.         synchronized (this) {    22.           WindowManagerImpl类关联着一个应用程序窗口视图对象(View对象)和一个ViewRoot对象的。一个View对象在与一个ViewRoot对象关联的同时,还会关联一个WindowManager.LayoutParams对象,这个WindowManager.LayoutParams对象是用来描述应用程序窗口视图的布局属性的。怎么关联呢?其实就是通过3个数组来关联的,WindowManagerImpl类有三个成员变量mViews、mRoots和mParams,它们分别是类型为View、ViewRootImpl和WindowManager.LayoutParams的数组。这三个数组的大小是始终保持相等的。这样, 有关联关系的View对象、ViewRootImpl对象和WindowManager.LayoutParams对象就会分别保存在数组mViews、mRoots和mParams的相同位置上,也就是说,mViews[i]、mRoots[i]和mParams[i]所描述的View对象、ViewRoot对象和WindowManager.LayoutParams对象是具有关联关系的,所以这里先从mViews中查找是否有对应的view,如果已经存在的话,那么就说明该View对象已经关联过ViewRoot对象以及WindowManager.LayoutParams对象了。在这种情况下,如果参数nest的值等于false,那么成员函数addView是不允许重复对参数view所描述的一个View对象进行重新关联的。另一方面,如果参数nest的值等于true,那么成员函数addView只是重新修改参数view所描述的一个View对象及其所关联的一个ViewRootImpl对象内部使用的一个WindowManager.LayoutParams对象,即更新为参数params所描述的一个WindowManager.LayoutParams对象,这是通过调用它们的成员函数setLayoutParams来实现的。  23.             int index = findViewLocked(view, false);    24.             if (index >= 0) {    25.                 if (!nest) {    26.                     throw new IllegalStateException("View " + view    27.                             + " has already been added to the window manager.");    28.                 }    29.                 root = mRoots[index];    30.                 root.mAddNesting++;    31.                 // Update layout parameters.    32.                 view.setLayoutParams(wparams);    33.                 root.setLayoutParams(wparams, true);    34.                 return;    35.             }    36.     37.             当参数view描述的是一个子应用程序窗口的视图对象时, 还需要找到这个子视图对象的父视图对象panelParentView  38.             if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&    39.                     wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {    40.                 final int count = mViews != null ? mViews.length : 0;    41.                 for (int i=0; i<count; i++) {    42.                     if (mRoots[i].mWindow.asBinder() == wparams.token) {    43.                         panelParentView = mViews[i];    44.                     }    45.                 }    46.             }    47.           通过view创建对应的ViewRootImpl对象  48.             root = new ViewRootImpl(view.getContext(), display);   49.             root.mAddNesting = 1;    50.     51.             view.setLayoutParams(wparams);    52.             53.             如果数组不存在,先创建,否则就继续扩充  54.             if (mViews == null) {    55.                 index = 1;    56.                 mViews = new View[1];    57.                 mRoots = new ViewRoot[1];    58.                 mParams = new WindowManager.LayoutParams[1];    59.             } else {    60.                 index = mViews.length + 1;    61.                 Object[] old = mViews;    62.                 mViews = new View[index];    63.                 System.arraycopy(old, 0, mViews, 0, index-1);    64.                 old = mRoots;    65.                 mRoots = new ViewRoot[index];    66.                 System.arraycopy(old, 0, mRoots, 0, index-1);    67.                 old = mParams;    68.                 mParams = new WindowManager.LayoutParams[index];    69.                 System.arraycopy(old, 0, mParams, 0, index-1);    70.             }    71.             index--;    72.     73.             mViews[index] = view;    74.             mRoots[index] = root;    75.             mParams[index] = wparams;    76.         }    77.         最后调用ViewRootImpl的setview方法   78.         root.setView(view, wparams, panelParentView);    79.     }    80.     81.     ......    82.     83.     private View[] mViews;    84.     private ViewRootImpl[] mRoots;    85.     private WindowManager.LayoutParams[] mParams;    86.     87.     ......    88. }  

4.ViewRootImp.setView

  1. public final class ViewRoot extends Handler implements ViewParent,    2.         View.AttachInfo.Callbacks {    3.     ......    4.     5.     final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();    6.     ......    7.     8.     View mView;    9.     ......    10.     11.     final View.AttachInfo mAttachInfo;    12.     ......    13.     14.     boolean mAdded;    15.     ......    16.     17.     public void setView(View view, WindowManager.LayoutParams attrs,    18.             View panelParentView) {    19.         synchronized (this) {    20.             分别将vie赋值给mView和mAttachInfo.mRootView  21.             if (mView == null) {    22.                 mView = view;    23.                 mWindowAttributes.copyFrom(attrs);    24.                 ......    25.     26.                 mAttachInfo.mRootView = view;    27.                 .......    28.              如果panelParentView 不为null,表示这个view是一个子窗口,那么就要拿到用来描述这个父应用程序窗口视图对象的一个类型为W的Binder本地对象的IBinder接口,以便可以保存在ViewRoot类的成员变量mAttachInfo所描述的一个AttachInfo的成员变量mPanelParentWindowToken中去。这样以后就可以知道ViewRoot类的成员变量mView所描述的一个子应用程序窗口视图所属的父应用程序窗口视图是什么了  29.                 if (panelParentView != null) {    30.                     mAttachInfo.mPanelParentWindowToken    31.                             = panelParentView.getApplicationWindowToken();    32.                 }    33. mAdded的值设置为true,表示当前正在处理的一个ViewRoot对象已经关联好一个View对象了  34.                 mAdded = true;    35.                 ......    36.   调用ViewRootImpl类的另外一个成员函数requestLayout来请求对应用程序窗口视图的UI作第一次布局(会调用view的 onMeasue,onlayout方法,ondraw方法),这里首先会判断checkThread()方法来检测,当前线程是不是UI线程  37.                 requestLayout();    38.                 ......    39.                 try {    40. 调用ViewRoot类的静态成员变量sWindowSession所描述的一个类型为Session的Binder代理对象的成员函数add来请求WindowManagerService增加一个WindowState对象,以便可以用来描述当前正在处理的一个ViewRoot所关联的一个应用程序窗口。mWindow其实也是一个W的binder对象,表示的是当前的activity,当WMS中就会创建W对应的windowState对象.wms通过这个对象就可以与这个activity交互  41.                     res = sWindowSession.add(mWindow, mWindowAttributes,    42.                             getHostVisibility(), mAttachInfo.mContentInsets,    43.                             mInputChannel);    44.                 } catch (RemoteException e) {    45.                     mAdded = false;    46.                     mView = null;    47.                     ......    48.                     throw new RuntimeException("Adding window failed", e);    49.                 } finally {    50.                     if (restore) {    51.                         attrs.restore();    52.                     }    53.                 }    54.     55.                 ......    56.             }    57.     58.             ......    59.         }    60.     }    61.     62.     ......    63. }  

这样窗口的视图创建过程在应用程序端的调用过程就结束了,接下来就是在WindowManagerService为其增加一个WindowState对象的过程,用来渲染Android应用程序窗口的Surface的创建过程,以及Android应用程序窗口的渲染过程。

窗口视图中UI元素的测量、布局和绘制过程以及绘图表面Surface的创建过程

在上面的ViewRootImp.setView中会调用ViewRootImpl类的另外一个成员函数requestLayout来请求对应用程序窗口视图的UI作第一次布局,这里其实就会调用View的measure,layout,draw方法分别来测量,布局,和将ui绘制到canvans画布上,然后请求WindowManagerService服务来创建绘图表面Surface,最后通过Surface将画布渲染出来,就可以看到界面效果了

1.ViewRootImpl .requestLayout

public final class ViewRootImpl implements ViewParent,        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {   @Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {//检查是不是在UI线程,不是就抛异常            checkThread();设置为true,表示应用程序进程的UI线程正在被请求执行一个UI布局操作            mLayoutRequested = true;进行UI布局的操作            scheduleTraversals();        }    }}

2.ViewRootImpl .scheduleTraversals

void scheduleTraversals() {mTraversalScheduled用来表示应用程序进程的UI线程是否已经调度了一个MSG_DO_SCHEDULE_CALLBACK消息        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();mTraversalRunnable是一个实现Runnable的子类,mChoreographer实际上就是将通过handler将消息发送到主线程,最后调用mTraversalRunnable的run方法            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            if (!mUnbufferedInputDispatch) {                scheduleConsumeBatchedInput();            }            notifyRendererOfFramePending();            pokeDrawLockIfNeeded();        }    }

3.ViewRootImpl .TraversalRunnable

final class TraversalRunnable implements Runnable {        @Override        public void run() {            doTraversal();        }    }

4.ViewRootImpl .doTraversal

  void doTraversal() {        if (mTraversalScheduled) {            mTraversalScheduled = false;            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);//实际上就是调用这个方法来处理逻辑            performTraversals();            if (mProfile) {                Debug.stopMethodTracing();                mProfile = false;            }        }    }

5.ViewRootImpl .performTraversals

private void performTraversals() {       将当前的视图对象赋值给host        final View host = mView; 如果host为null,或者没有关联好一个ViewRootImpl对象 if (host == null || !mAdded)            return;WindowManager.LayoutParams lp = mWindowAttributes;layoutRequested为true那么就表示应用程序进程的UI线程正在被请求对当前正在处理的应用程序窗口执行一个UI布局操作,就调用measureHierarchy来测量各个层次的UI控件的大小,其实这里就是调用host的measure方法. if (layoutRequested) {   final Resources res = mView.getContext().getResources();            windowSizeMayChange |= measureHierarchy(host, lp, res,                    desiredWindowWidth, desiredWindowHeight);}调用relayoutWindow来请求WindowManagerService服务重新布局系统中的所有窗口,并且创建出该view在wms所需要的surface对象.relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); if (didLayout) {//调用View的layout            performLayout(lp, desiredWindowWidth, desiredWindowHeight);}   if (!cancelDraw && !newSurface) {//调用View的draw将ui绘制到画布canvans上,然后在调用surface.unlockCanvasAndPost(canvas)将画布渲染出来                performDraw();            }        } else {            if (viewVisibility == View.VISIBLE) {                // Try again                scheduleTraversals();            } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {                for (int i = 0; i < mPendingTransitions.size(); ++i) {                    mPendingTransitions.get(i).endChangingAnimations();                }                mPendingTransitions.clear();            }        }}

在这一步会分别调用measure,layout,draw方法测量,布局,绘制,然后在调用surface.unlockCanvasAndPost(canvas)将画布渲染出来,所以下面我们就先看看surface是如何创建出来的,然后在看看View的measure,layout,draw方法.

6.ViewRootImpl .relayoutWindow

  1.  private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,      2.             boolean insetsPending) throws RemoteException {      3.         ......      4.       5.         int relayoutResult = sWindowSession.relayout(      6.                 mWindow, params,      7.                 (int) (mView.mMeasuredWidth * appScale + 0.5f),      8.                 (int) (mView.mMeasuredHeight * appScale + 0.5f),      9.                 viewVisibility, insetsPending, mWinFrame,      10.                 mPendingContentInsets, mPendingVisibleInsets,      11.                 mPendingConfiguration, mSurface);      12.         ......      13.       14.         return relayoutResult;   15.     }

relayoutWindow调用静态成员变量sWindowSession所描述的一个实现了IWindowSession接口的Binder代理对象的成员函数relayout来请求WindowManagerService服务对成员变量mWindow所描述的一个应用程序窗口的UI进行重新布局,同时,还会将成员变量mSurface所描述的一个Surface对象传递给WindowManagerService服务,以便WindowManagerService服务可以根据需要来重新创建一个绘图表面给成员变量mWindow所描述的一个应用程序窗口使用。
7.IWindowSession .Proxy .relayout

  1. public interface IWindowSession extends android.os.IInterface    2. {    3.     ......    4.     5.     public static abstract class Stub extends android.os.Binder implements android.view.IWindowSession    6.     {    7.          ......    8.     9.          private static class Proxy implements android.view.IWindowSession    10.          {    11.              private android.os.IBinder mRemote;    12.              ......    13.     14.              public int relayout(android.view.IWindow window, android.view.WindowManager.LayoutParams attrs,     15.                    int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending,     16.                    android.graphics.Rect outFrame,     17.                    android.graphics.Rect outContentInsets,     18.                    android.graphics.Rect outVisibleInsets,     19.                    android.content.res.Configuration outConfig,     20.                    android.view.Surface outSurface) throws android.os.RemoteException    21.             {    22.                 android.os.Parcel _data = android.os.Parcel.obtain();    23.                 android.os.Parcel _reply = android.os.Parcel.obtain();    24.     25.                 int _result;    26.                 try {    27.                     _data.writeInterfaceToken(DESCRIPTOR);    28.                     _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));    29.     30.                     if ((attrs!=null)) {    31.                         _data.writeInt(1);    32.                         attrs.writeToParcel(_data, 0);    33.                     }    34.                     else {    35.                         _data.writeInt(0);    36.                     }    37.     38.                     _data.writeInt(requestedWidth);    39.                     _data.writeInt(requestedHeight);    40.                     _data.writeInt(viewVisibility);    41.                     _data.writeInt(((insetsPending)?(1):(0)));    42.                        43.                     mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);    44.                     45.                     _reply.readException();    46.                     _result = _reply.readInt();    47.     48.                     if ((0!=_reply.readInt())) {    49.                         outFrame.readFromParcel(_reply);    50.                     }    51.     52.                     if ((0!=_reply.readInt())) {    53.                         outContentInsets.readFromParcel(_reply);    54.                     }    55.     56.                     if ((0!=_reply.readInt())) {    57.                         outVisibleInsets.readFromParcel(_reply);    58.                     }    59.     60.                     if ((0!=_reply.readInt())) {    61.                         outConfig.readFromParcel(_reply);    62.                     }    63.     64.                     if ((0!=_reply.readInt())) {    65.                         outSurface.readFromParcel(_reply);    66.                     }    67.     68.                 } finally {    69.                     _reply.recycle();    70.                     _data.recycle();    71.                 }    72.     73.                 return _result;    74.             }    75.     76.             ......    77.         }    78.     79.         ......    80.     }    81.     82.     ......    83. }  

当运行在WindowManagerService服务内部的Session对象处理完成当前应用程序进程发送过来的类型为TRANSACTION_relayout的进程间通信请求之后,就会将处理结果写入到Parcel对象_reply中,并且将这个Parcel对象_reply返回给当前应用程序进程处理。返回结果包含了一系列与参数window所描述的应用程序窗口相关的参数,如下所示:
1. 窗口的大小:最终保存在输出参数outFrame中。
2. 窗口的内容区域边衬大小:最终保存在输出参数outContentInsets中。
3. 窗口的可见区域边衬大小:最终保存在输出参数outVisibleInsets中。
4. 窗口的配置信息:最终保存在输出参数outConfig中。
5. 窗口的绘图表面:最终保存在输出参数outSurface中。

8.WindowManagerService.relayoutWindow

  1. public int relayoutWindow(Session session, IWindow client,    2.             WindowManager.LayoutParams attrs, int requestedWidth,    3.             int requestedHeight, int viewVisibility, boolean insetsPending,    4.             Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,    5.             Configuration outConfig, Surface outSurface) {          获得与参数client所对应的一个WindowState对象win  1.  WindowState win = windowForClientLocked(session, client, false);    2.          如果win为null,表示该窗口不存在,返回一个0值给应用程序进程  3.             if (win == null) {    4.                 return 0;    5.             }    6.        acitivty是否是可见的,只有在可见的状态下才会创建Surface  7.             if (viewVisibility == View.VISIBLE &&    8.                     (win.mAppToken == null || !win.mAppToken.clientHidden)) {    9.                 ......    10.     6.         ......    try {    7.                    创建一个绘图表面surface  8.                     Surface surface = win.createSurfaceLocked();    9.                     if (surface != null) {    10.                         outSurface.copyFrom(surface);    11.                         ......    12.                     } else {    13.                         // For some reason there isn't a surface.  Clear the    14.                         // caller's object so they see the same state.    15.                         outSurface.release();    16.                     }    17.                 } catch (Exception e) {    18.                     ......    19.                     return 0;    20.                 }    21. }

WindowManagerService类的成员函数relayoutWindow根据应用程序进程传递过来的一系列数据来重新设置由参数client所描述的一个应用程序窗口的大小和可见性等信息,而当一个应用程序窗口的大小或者可见性发生变化之后,系统中当前获得焦点的窗口,以及输入法窗口和壁纸窗口等都可能会发生变化,而且也会对其它窗口产生影响,因此,这时候WindowManagerService类的成员函数relayoutWindow就会对系统中的窗口的布局进行重新调整。

9.WindowState.createSurfaceLocked

 private final class WindowState implements WindowManagerPolicy.WindowState {         Surface createSurfaceLocked() {         参数分别是与应用程序窗口它所运行的应用程序进程所关联的一个SurfaceSession对象, 应用程序窗口它所运行的应用程序进程的PID,应用程序窗口的标题,应用程序窗口的像素格式, 应用程序窗口的宽度, 应用程序窗口的高度。 应用程序窗口的图形缓冲区属性标志  1.   mSurface = new Surface(    2.                             mSession.mSurfaceSession, mSession.mPid,    3.                             mAttrs.getTitle().toString(),    4.                             0, w, h, mAttrs.format, flags);   5. }  6. }9.Surface的构造方法  1. public class Surface implements Parcelable {    2.     ......    3.     4.     private int mSurfaceControl;    5.     ......    6.     private Canvas mCanvas;    7.     ......    8.     private String mName;    9.     ......    10.     11.     public Surface(SurfaceSession s,    12.             int pid, String name, int display, int w, int h, int format, int flags)    13.         throws OutOfResourcesException {    14.         ......    15.     16.         mCanvas = new CompatibleCanvas();    17.         init(s,pid,name,display,w,h,format,flags);    18.         mName = name;    19.     }    20.     21.     ......    22.     23.     private native void init(SurfaceSession s,    24.             int pid, String name, int display, int w, int h, int format, int flags)    25.             throws OutOfResourcesException;    26.     27.     ......    28. } 

Surface类有三个成员变量mSurfaceControl、mCanvas和mName,它们的类型分别是int、Canvas和mName,其中,mSurfaceControl保存的是在C++层的一个SurfaceControl对象的地址值,mCanvas用来描述一块类型为CompatibleCanvas的画布,mName用来描述当前正在创建的一个绘图表面的名称。画布是真正用来绘制UI的地方.

Surface类的构造函数是通过调用另外一个成员函数init来创建与成员变量mSurfaceControl所关联的C++层的SurfaceControl对象的。Surface类的成员函数init是一个JNI方法,它是由C++层的函数Surface_init来实现的,如下所示:

  1. static void Surface_init(    2.         JNIEnv* env, jobject clazz,    3.         jobject session,    4.         jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)    5. {    6.     if (session == NULL) {    7.         doThrow(env, "java/lang/NullPointerException");    8.         return;    9.     }    10.     11.     SurfaceComposerClient* client =    12.             (SurfaceComposerClient*)env->GetIntField(session, sso.client);    13.     14.     sp<SurfaceControl> surface;    15.     if (jname == NULL) {    16.         surface = client->createSurface(pid, dpy, w, h, format, flags);    17.     } else {    18.         const jchar* str = env->GetStringCritical(jname, 0);    19.         const String8 name(str, env->GetStringLength(jname));    20.         env->ReleaseStringCritical(jname, str);    21.         surface = client->createSurface(pid, name, dpy, w, h, format, flags);    22.     }    23.     24.     if (surface == 0) {    25.         doThrow(env, OutOfResourcesException);    26.         return;    27.     }    28.     setSurfaceControl(env, clazz, surface);    29. }

参数session指向了在Java层所创建的一个SurfaceSession对象。Java层的SurfaceSession对象有一个成员变量mClient,它指向了在C++层中的一个SurfaceComposerClient对象。C++层的SurfaceComposerClient对象可以用来请求SurfaceFlinger服务为应用程序窗口创建绘图表面,即创建一个Layer对象。因此,函数首先将参数session所指向的一个Java层的SurfaceSession对象的成员变量mClient转换成一个SurfaceComposerClient对象,然后再调用这个SurfaceComposerClient对象的成员函数createSurface来请求SurfaceFlinger服务来为参数clazz所描述的一个Java层的Surface对象所关联的应用程序窗口创建一个Layer对象。SurfaceFlinger服务创建完成这个Layer对象之后,就会将该Layer对象内部的一个实现了ISurface接口的SurfaceLayer对象返回给函数,于是,函数就可以获得一个实现了ISurface接口的Binder代理对象。这个实现了ISurface接口的Binder代理对象被封装在C++层的一个SurfaceControl对象surface中。

通过上面的分析可以得知:
1. 每一个应用程序窗口都对应有两个Java层的Surface对象,其中一个是在WindowManagerService服务这一侧创建的,而另外一个是在应用程序进程这一侧创建的。
2. 在WindowManagerService服务这一侧创建的Java层的Surface对象在C++层关联有一个SurfaceControl对象,用来设置应用窗口的属性,例如,大小和位置等。
3. 在应用程序进程这一侧创建的ava层的Surface对象在C++层关联有一个Surface对象,用来绘制应用程序窗口的UI。
分析完了surface的创建过程下面就来看看View的measure,layout,draw是如何来对UI进行测量,布局和绘制的.

10.View.measure

  1. public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {    2.     ......    3.     4.     int mPrivateFlags;    5.     ......    6.   上一次可以获得的最大宽度  7.     int mOldWidthMeasureSpec = Integer.MIN_VALUE;    8.     ......    9.   上一次可以获得的最大高度  10.     int mOldHeightMeasureSpec = Integer.MIN_VALUE;    11.     ......   参数widthMeasureSpec和heightMeasureSpec用来描述当前正在处理的视图可以获得的最大宽度和高度。对于应用程序窗口的顶层视图来说,我们也可以认为这两个参数是用来描述应用程序窗口的宽度和高度。  public final void measure(int widthMeasureSpec, int heightMeasureSpec) {        boolean optical = isLayoutModeOptical(this);成员变量mPrivateFlags的FORCE_LAYOUT位不等于0时,就表示当前视图正在请求执行一次布局操作,这时候函数就需要重新测量当前视图的宽度和高度。或者当参数widthMeasureSpec和heightMeasureSpec的值不等于mOldWidthMeasureSpec 和mOldHeightMeasureSpec 就表示当前视图上一次可以获得的最大宽度和高度已经失效了,这时候函数也需要重新测量当前视图的宽度和高度。        if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||                widthMeasureSpec != mOldWidthMeasureSpec ||                heightMeasureSpec != mOldHeightMeasureSpec) {            它就会首先将成员变量mPrivateFlags的MEASURED_DIMENSION_SET位设置为0            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;            resolveRtlPropertiesIfNeeded();            int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :                    mMeasureCache.indexOfKey(key);            if (cacheIndex < 0 || sIgnoreMeasureCache) {         调用另外一个成员函数onMeasure来真正执行测量宽度和高度的操作,在这个函数中会调用setMeasuredDimension来将测量好的宽度和高度设置到View类的成员变量mMeasuredWidth和mMeasuredHeight中,因为当前视图最终就是通过View类的成员变量mMeasuredWidth和mMeasuredHeight来获得它的宽度和高度的.所以在自定义View的时候一般在最后调用super.onMeasure或者直接调用setMeasuredDimension方法都可以                onMeasure(widthMeasureSpec, heightMeasureSpec);                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            } else {                long value = mMeasureCache.valueAt(cacheIndex);                // Casting a long to int drops the high 32 bits, no mask needed                setMeasuredDimensionRaw((int) (value >> 32), (int) value);                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            }            if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {                throw new IllegalStateException("View with id " + getId() + ": "                        + getClass().getName() + "#onMeasure() did not set the"                        + " measured dimension by calling"                        + " setMeasuredDimension()");            }            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;        }把参数widthMeasureSpec和heightMeasureSpec的值保存在成员变量mldWidthMeasureSpec和mOldHeightMeasureSpec中,以便可以记录当前视图上一次可以获得的最大宽度和高度。        mOldWidthMeasureSpec = widthMeasureSpec;        mOldHeightMeasureSpec = heightMeasureSpec;        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension    }}

11.View.onMeasure

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }

一般view都是会重写onMeasure方法,调用完这个方法也就完成了所有UI元素的测量,下面就是进入到布局了.

12.View.layout

  1. public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {    2.     ......    3.      4.     int mPrivateFlags;    5.     ......    6.     7.     public final void layout(int l, int t, int r, int b) {    8.         boolean changed = setFrame(l, t, r, b);    9.         if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {    10.             ......    11.     12.             onLayout(changed, l, t, r, b);    13.             mPrivateFlags &= ~LAYOUT_REQUIRED;    14.         }    15.         mPrivateFlags &= ~FORCE_LAYOUT;    16.     }    17.     18.     ......    19. } 

参数l、t、r和b分别用来描述当前视图的左上右下四条边与其父视图的左上右下四条边的距离,这样当前视图通过这四个参数就可以知道它在父视图中的位置以及大小。
View类的成员函数layout首先调用另外一个成员函数setFrame来设置当前视图的位置以及大小。设置完成之后,如果当前视图的大小或者位置与上次相比发生了变化,那么View类的成员函数setFrame的返回值changed就会等于true。在这种情况下, View类的成员函数layout就会继续调用另外一个成员函数onLayout重新布局当前视图的子视图。此外,如果此时View类的成员变量mPrivateFlags的LAYOUT_REQUIRED位不等于0,那么也表示当前视图需要重新布局它的子视图,因此,这时候View类的成员函数layout也会调用另外一个成员函数onLayout。
当前视图的子视图都重新布局完成之后,View类的成员函数layout就可以将成员变量mPrivateFlags的LAYOUT_REQUIRED位设置为0了,因为此时当前视图及其子视图都已经执行了一次布局操作了。
View类的成员函数layout最后还会将成员变量mPrivateFlags的FORCE_LAYOUT位设置为0,也是因为此时当前视图及其子视图的布局已经是最新的了。

13.View.setFrame

  1. public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {    2.     ......    3.      4.     int mPrivateFlags;    5.     ......    6.     7.     int mViewFlags;    8.     ......    9.     10.     protected int mLeft;    11.     ......    12.     13.     protected int mRight;    14.     ......    15.     16.     protected int mTop;    17.     ......    18.     19.     protected int mBottom;    20.     ......    21.     22.     private boolean mBackgroundSizeChanged;    23.     ......    24.     25.     protected boolean setFrame(int left, int top, int right, int bottom) {    26.         boolean changed = false;    27.     28.         ......    29.     30.         if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {    31.             changed = true;    32.     33.             // Remember our drawn bit    34.             int drawn = mPrivateFlags & DRAWN;    35.     36.            检查当前视图上次请求的UI绘制操作是否已经执行。如果已经执行了的话,那么就会再请求执行一个UI绘制操作,以便可以在修改当前视图的大小和位置之前,将当前视图在当前位置按照当前大小显示一次。   37.             invalidate();    38.     39.   计算当前视图上一次的宽度oldWidth和oldHeight,以便接下来可以检查当前视图的大小是否发生了变化。  40.             int oldWidth = mRight - mLeft;    41.             int oldHeight = mBottom - mTop;    42.     43.             mLeft = left;    44.             mTop = top;    45.             mRight = right;    46.             mBottom = bottom;    47.     48.             mPrivateFlags |= HAS_BOUNDS;    49.     50.             int newWidth = right - left;    51.             int newHeight = bottom - top;    52.   计算当前视图新的宽度newWidth和高度newHeight,如果它们与上一次的宽度oldWidth和oldHeight的值不相等,那么就说明当前视图的大小发生了变化,这时候就会调用另外一个成员函数onSizeChanged来让子类有机会处理这个变化事件。  53.             if (newWidth != oldWidth || newHeight != oldHeight) {    54.                 onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);    55.             }    56.     57.             if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {    58.              View类的成员函数setFrame接下来继续判断当前视图是否是可见的,即成员变量mViewFlags的VISIBILITY_MASK位的值是否等于VISIBLE。如果是可见的话,那么就需要将成员变量mPrivateFlags的DRAWN位设置为1,以便接下来可以调用另外一个成员函数invalidate来成功地执行一次UI绘制操作,目的是为了将当前视图马上显示出来。   59.                 mPrivateFlags |= DRAWN;    60.                 invalidate();    61.             }    62.     63.             // Reset drawn bit to original value (invalidate turns it off)    64.             mPrivateFlags |= drawn;    65.   当前视图的大小和位置发生了变化,View类的成员函数setFrame还会将成员变量mBackgroundSizeChanged的值设置为true,以便可以表示当前视图的背景大小发生了变化。  66.             mBackgroundSizeChanged = true;    67.         }    68.         return changed;    69.     }    70.     71.     ......    72. }  

14.View.invalidate

  1. public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {    2.     ......    3.     4.     protected ViewParent mParent;    5.     ......    6.      7.     int mPrivateFlags;    8.     ......        9.     10.     public void invalidate() {    11.         ......    12.    View类的成员函数invalidate首先检查成员变量mPrivateFlags的DRAWN位和HAS_BOUNDS位是否都被设置为1。如果是的话,那么就说明当前视图上一次请求执行的UI绘制操作已经执行完成了,这时候View类的成员函数invalidate才可以请求执行新的UI绘制操作。  13.         if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {    14.             mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;    15.             final ViewParent p = mParent;    16.             final AttachInfo ai = mAttachInfo;    17.             if (p != null && ai != null) {    18.                 final Rect r = ai.mTmpInvalRect;    19.                 r.set(0, 0, mRight - mLeft, mBottom - mTop);    20.              请求绘制当前视图的UI是通过调用View类的成员变量mParent所描述的一个ViewParent接口的成员函数invalidateChild来实现的,假设当前视图是应用程序窗口的顶层视图,即它是一个类型为DecoreView的视图,它的成员变量mParent指向的是与其所关联的一个ViewRootImpl对象,因此,绘制当前视图的UI的操作实际上是通过调用ViewRootImpl类的成员函数invalidateChild来实现的  21.                 p.invalidateChild(this, r);    22.             }    23.         }    24.     }    25.     26.     ......    27. }  

15.ViewRootImpl.invalidateChild

  public void invalidateChild(View child, Rect dirty) {        invalidateChildInParent(null, dirty);    }    @Override    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {//首先检查当前线程是否是UI线程        checkThread();        if (dirty == null) {            invalidate();            return null;        } else if (dirty.isEmpty() && !mIsAnimating) {            return null;        } ViewRoot类的成员函数invalidateChild接下来还会检查当前正在处理的应用程序窗口在Y轴上是否出现有滚动条,即成员变量mCurScrollY的值不等于0, 或者前正在处理的应用程序窗口是否运行在兼容模式之下,即成员变量mTranslator的值不等于null。当一个应用程序窗口运行在兼容模式时,它显示出来的大小和它实际被设置的大小是不一样的,要经过相应的转换处理。对于上述这两种情况,ViewRoot类的成员函数invalidateChild都需要调整参数dirty所描述的一个需要重新绘制的区域的大小和位置。        if (mCurScrollY != 0 || mTranslator != null) {            mTempRect.set(dirty);            dirty = mTempRect;            if (mCurScrollY != 0) {                dirty.offset(0, -mCurScrollY);            }            if (mTranslator != null) {                mTranslator.translateRectInAppWindowToScreen(dirty);            }            if (mAttachInfo.mScalingRequired) {                dirty.inset(-1, -1);            }        }调整好参数dirty所描述的一个需要重新绘制的区域之后,就会调用下面的方法进行总的绘制        invalidateRectOnScreen(dirty);        return null;    } private void invalidateRectOnScreen(Rect dirty) {        final Rect localDirty = mDirty;        if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {            mAttachInfo.mSetIgnoreDirtyState = true;            mAttachInfo.mIgnoreDirtyState = true;        }        // Add the new dirty rect to the current one        localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);        // Intersect with the bounds of the window to skip        // updates that lie outside of the visible region        final float appScale = mAttachInfo.mApplicationScale;        final boolean intersected = localDirty.intersect(0, 0,                (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));        if (!intersected) {            localDirty.setEmpty();        }检查成员变量mWillDrawSoon的值是否不等于true。如果ViewRoot类的成员mWillDrawSoon的值等于true的话,那么就说明UI线程的消息队列中已经有一个消息在等待执行了,这时候就不需要调用ViewRootImpl类的成员函数scheduleTraversals来向UI线程的消息队列发送一个消息了,否则的话,就需要调用ViewRoot类的成员函数scheduleTraversals来向UI线程的消息队列发送一个消息来调用draw完成UI绘制.        if (!mWillDrawSoon && (intersected || mIsAnimating)) {            scheduleTraversals();        }    }

调用完setFrame设置当前视图的位置以及大小,设置完成之后,如果当前视图的大小或者位置与上次相比发生了变化,那么View类的成员函数setFrame的返回值changed就会等于true。在这种情况下, View类的成员函数layout就会继续调用另外一个成员函数onLayout重新布局当前视图的子视图,接下来就看下onLayout方法.

16.View.onLayout

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {    }

View的onLyaout是一个空实现,实际都是调用view重写的这个方法来完成UI的布局.布局完成了最后就剩下一个绘制过程了

17.View.draw

 public void draw(Canvas canvas) {这段代码检查View类的成员变量mPrivateFlags的DIRTY_OPAQUE位是否等于1。如果等于1的话,那么就说明当前视图的某一个子视图请求了一个不透明UI绘制操作。在这种情况下,当前视图会被子视图覆盖,因此,就不需要执行绘制当前视图的背景和视图内容了.        final int privateFlags = mPrivateFlags;不过要想真的不绘制当前视图的背景和内容还需要满足下面的条件让dirtyOpaque =true,        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);将成员变量mPrivateFlags的DIRTY_MASK位重置为0,以及将DRAWN位设置为1,因为接下来就要开始绘制当前视图的UI了        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;        /*         * Draw traversal performs several drawing steps which must be executed         * in the appropriate order:         *         *      1. Draw the background  绘制当前视图的背景         *      2. If necessary, save the canvas' layers to prepare for fading   如果有需要保存当前画布的堆栈状态,并且在在当前画布上创建额外的图层,以便接下来可以用来绘制当前视图在滑动时的边框渐变效果。         *      3. Draw view's content  绘制当前视图的内容。         *      4. Draw children  绘制当前视图的子视图的内容。         *      5. If necessary, draw the fading edges and restore layers   如果有需要绘制当前视图在滑动时的边框渐变效果。         *      6. Draw decorations (scrollbars for instance)   绘制当前视图的滚动条。         */        // Step 1, draw the background, if needed        int saveCount;这里就根据上面一开始检查结果来判断是否要绘制当前视图的背景        if (!dirtyOpaque) {            drawBackground(canvas);        }        // skip step 2 & 5 if possible (common case)        final int viewFlags = mViewFlags;当View类的成员变量mViewFlags的FADING_EDGE_HORIZONTAL位等于1的时候,就说明当前视图正在处于水平滑动状态,并且需要绘制水平边框的渐变效果。同样,当View类的成员变量mViewFlags的FADING_EDGE_VERTICAL位等于1的时候,就说明当前视图正在处于垂直滑动状态,并且需要绘制垂直边框的渐变效果。但是,如果当前视图不是处于滑动状态,即变量horizontalEdges和verticalEdges的值均等于false的时候,那么就不需要执行上述的第2和第5个操作了,        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;        if (!verticalEdges && !horizontalEdges) {            // Step 3, draw the content根据一开始检查结果是否要绘制当前视图内容            if (!dirtyOpaque) onDraw(canvas);            // Step 4, draw the children绘制子视图            dispatchDraw(canvas);            // Overlay is part of the content and draws beneath Foreground            if (mOverlay != null && !mOverlay.isEmpty()) {                mOverlay.getOverlayView().dispatchDraw(canvas);            }            // Step 6, draw decorations (foreground, scrollbars) 绘制当前视图的滚动条。            onDrawForeground(canvas);            // we're done...            return;        } //如果能执行到这里,表示要绘制第二步和第五步.所以就从2开始绘制到6        boolean drawTop = false;        boolean drawBottom = false;        boolean drawLeft = false;        boolean drawRight = false;        float topFadeStrength = 0.0f;        float bottomFadeStrength = 0.0f;        float leftFadeStrength = 0.0f;        float rightFadeStrength = 0.0f;        // Step 2, save the canvas' layers        int paddingLeft = mPaddingLeft;        final boolean offsetRequired = isPaddingOffsetRequired();        if (offsetRequired) {            paddingLeft += getLeftPaddingOffset();        }        int left = mScrollX + paddingLeft;        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;        int top = mScrollY + getFadeTop(offsetRequired);        int bottom = top + getFadeHeight(offsetRequired);        if (offsetRequired) {            right += getRightPaddingOffset();            bottom += getBottomPaddingOffset();        }        final ScrollabilityCache scrollabilityCache = mScrollCache;        final float fadeHeight = scrollabilityCache.fadingEdgeLength;        int length = (int) fadeHeight;        // clip the fade length if top and bottom fades overlap        // overlapping fades produce odd-looking artifacts        if (verticalEdges && (top + length > bottom - length)) {            length = (bottom - top) / 2;        }        // also clip horizontal fades if necessary        if (horizontalEdges && (left + length > right - length)) {            length = (right - left) / 2;        }        if (verticalEdges) {            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));            drawTop = topFadeStrength * fadeHeight > 1.0f;            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;        }        if (horizontalEdges) {            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));            drawLeft = leftFadeStrength * fadeHeight > 1.0f;            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));            drawRight = rightFadeStrength * fadeHeight > 1.0f;        }        saveCount = canvas.getSaveCount();        int solidColor = getSolidColor();        if (solidColor == 0) {            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;            if (drawTop) {                canvas.saveLayer(left, top, right, top + length, null, flags);            }            if (drawBottom) {                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);            }            if (drawLeft) {                canvas.saveLayer(left, top, left + length, bottom, null, flags);            }            if (drawRight) {                canvas.saveLayer(right - length, top, right, bottom, null, flags);            }        } else {            scrollabilityCache.setFadeColor(solidColor);        }        // Step 3, draw the content        if (!dirtyOpaque) onDraw(canvas);        // Step 4, draw the children        dispatchDraw(canvas);        // Step 5, draw the fade effect and restore layers        final Paint p = scrollabilityCache.paint;        final Matrix matrix = scrollabilityCache.matrix;        final Shader fade = scrollabilityCache.shader;        if (drawTop) {            matrix.setScale(1, fadeHeight * topFadeStrength);            matrix.postTranslate(left, top);            fade.setLocalMatrix(matrix);            p.setShader(fade);            canvas.drawRect(left, top, right, top + length, p);        }        if (drawBottom) {            matrix.setScale(1, fadeHeight * bottomFadeStrength);            matrix.postRotate(180);            matrix.postTranslate(left, bottom);            fade.setLocalMatrix(matrix);            p.setShader(fade);            canvas.drawRect(left, bottom - length, right, bottom, p);        }        if (drawLeft) {            matrix.setScale(1, fadeHeight * leftFadeStrength);            matrix.postRotate(-90);            matrix.postTranslate(left, top);            fade.setLocalMatrix(matrix);            p.setShader(fade);            canvas.drawRect(left, top, left + length, bottom, p);        }        if (drawRight) {            matrix.setScale(1, fadeHeight * rightFadeStrength);            matrix.postRotate(90);            matrix.postTranslate(right, top);            fade.setLocalMatrix(matrix);            p.setShader(fade);            canvas.drawRect(right - length, top, right, bottom, p);        }        canvas.restoreToCount(saveCount);        // Overlay is part of the content and draws beneath Foreground        if (mOverlay != null && !mOverlay.isEmpty()) {            mOverlay.getOverlayView().dispatchDraw(canvas);        }        // Step 6, draw decorations (foreground, scrollbars)        onDrawForeground(canvas);    }

在第4步绘制子视图的时候,是调用view的dispatchDraw,而view的dispatchDraw是一个空实现,它是由子类ViewGroup来重写的,也就是说,只有当一个视图描述的是一个视图容器时,它才会重写父类View的成员函数dispatchDraw。看看ViewGroup的dispatchDraw

18.ViewGroup.dispatchDraw

protected void dispatchDraw(Canvas canvas) {ViewGroup类的成员变量mChildrenCount描述的是当前视图组的子视图的个数,另外一个成员变量mChildren是一个类型为View的数组,用来保存当前视图组的子视图  1.   final int count = mChildrenCount;    2.         final View[] children = mChildren;    3.         int flags = mGroupFlags; 这段代码用来检查当前视图组的子视图是否需要显示动画。如果变量flags的FLAG_RUN_ANIMATION位等于1,并且ViewGroup类的成员函数canAnimate的返回值等于true,即当前当前视图组允许其子视图显示动画,那么这段代码接下来就要开始显示动画了。  1. if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {    2.     ......    3.     4.     for (int i = 0; i < count; i++) {    5.         final View child = children[i];    6. 首先检查当前视图组的每一个子视图child,如果它是可见的  7.         if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {    8.             ......    9.          设置child的动画  10.             bindLayoutAnimation(child);    11.             ......    12.         }    13.     }    14.     15.     final LayoutAnimationController controller = mLayoutAnimationController;    16.     ......    17.       启动动画  18.     controller.start();    19.     20.     ......    21.     22.     if (mAnimationListener != null) {    23.       通知那些注册到当前视图组的动画监听者,当前视图组开始显示动画了  24.         mAnimationListener.onAnimationStart(controller.getAnimation());    25.     }    26. }   这段代码检查变量flags的CLIP_TO_PADDING_MASK位是否不等于1。如果不等于1的话,那么就说明需要设置参数canvas所描述的一块画布的剪裁区域,使得这个裁剪区域不包含当前视图组的内边距。注意,当前视图组的内边距是通过从父类View继承下来的四个成员变量mPaddingLeft、mPaddingRight、mPaddingTop和mPaddingBottom来描述的。此外,当前视图组的区域是通过从父类继承下来的四个成员变量量mLeft、mRight、mTop和mBottom描述的。再结合当前视图的当前滚动位置mScrollX的mScrollY,就可以计算出参数canvas所描述的一块画布的剪裁区域。  27. int saveCount = 0;    28. final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;    29. if (clipToPadding) {    30. 保存它的堆栈状态,以便在绘制完成当前视图组的UI之后,可以恢复canvas所描述的一块画布的堆栈状态  31.     saveCount = canvas.save();    32.   设置裁剪区域  33.     canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,    34.             mScrollX + mRight - mLeft - mPaddingRight,    35.             mScrollY + mBottom - mTop - mPaddingBottom);    36.     37. }   38.   这段代码用来绘制当前视图组的子视图。如果一个子视图child是可见的,即它的成员变量mViewFlags的VISIBLE位等于1,或者它有一个动画需要显示,即它的成员函数getAnimation的返回值不等于null,那么这个子视图就是需要绘制的。绘制一个子视图是通过调用ViewGroup类的成员函数drawChild来实现的。ViewGroup类的成员函数drawChild在绘制一个子视图的时候,如果这个子视图的动画还没有结束,那么它的返回值就等于true,并且会被设置到变量more中去  1. boolean more = false;    2. final long drawingTime = getDrawingTime();    3.     4. if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {    5.     for (int i = 0; i < count; i++) {    6.         final View child = children[i];    7.         if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {    8.             more |= drawChild(canvas, child, drawingTime);    9.         }    10.     }    11. } else {    12.     for (int i = 0; i < count; i++) {    13.         final View child = children[getChildDrawingOrder(count, i)];    14.         if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {    15.             more |= drawChild(canvas, child, drawingTime);    16.         }    17.     }    18. } ViewGroup类的成员变量mDisappearingChildren用来保存那些正在消失的子视图,但是这些子视图正在显示动画的过程中,因此,这些子视图也是需要绘制的,这段代码同样是通过调用ViewGroup类的成员函数drawChild来绘制它们。  19. // Draw any disappearing views that have animations    20. if (mDisappearingChildren != null) {    21.     final ArrayList<View> disappearingChildren = mDisappearingChildren;    22.     final int disappearingCount = disappearingChildren.size() - 1;    23.     // Go backwards -- we may delete as animations finish    24.     for (int i = disappearingCount; i >= 0; i--) {    25.         final View child = disappearingChildren.get(i);    26.         more |= drawChild(canvas, child, drawingTime);    27.     }    28. } . 检查变量clipToPadding的值是否等于true。如果是的话,那么就说明前面设置过参数canvas所描述的一块画布的裁剪区域  29.  if (clipToPadding) {    30. 通过调用参数canvas所指向的一个Canvas对象的成员函数restoreToCount来恢复参数canvas所描述的一块画布堆栈状态  31.         canvas.restoreToCount(saveCount);    32.     }    33.     34.     // mGroupFlags might have been updated by drawChild()    35.     flags = mGroupFlags;    36.   前面在绘制当前视图组的子视图的UI的时候,有可能会需要修改当前视图组的标志位,即修改ViewGroup类的成员变量mGroupFlags的值。如果修改后的mGroupFlags的FLAG_INVALIDATE_REQUIRED位等于1,那么就说明当前视图组需要重新发起一个绘制UI的请求。这是通过调用ViewGroup类的另外一个成员函数invalidate来实现的。  37.     if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {    38.         invalidate();    39.     }    40.    如果当前视图组的动画已经显示完成,并且当前视图组的子视图的动画也已经显示完成,再并且当前视图组注册有动画监听者,那么就是会调用ViewGroup类的另外一个成员函数notifyAnimationListener来通知这些动画监听者,当前视图组的动画已经显示结束。注意,ViewGroup类的成员函数notifyAnimationListener是以消息的形式来调用的,即ViewGroup类的成员函数dispatchDraw不是在动画一显示结束,就马上通知那些动画监听者。    if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&    41.             mLayoutAnimationController.isDone() && !more) {    42.         // We want to erase the drawing cache and notify the listener after the    43.         // next frame is drawn because one extra invalidate() is caused by    44.         // drawChild() after the animation is over    45.         mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;    46.         final Runnable end = new Runnable() {    47.            public void run() {    48.                notifyAnimationListener();    49.            }    50.         };    51.         post(end);    52.     }    53. } 

到这一步UI都绘制到了canvas画布上了,这时界面上还是看不到效果, 还需要将画布显示到Surface上,在requestlayout的performDraw中调用完View的draw(canvas)之后,就会调用surface.unlockCanvasAndPost(canvas)来请求SurfaceFlinger服务渲染这块画布里面所包含的一个图形缓冲区了.实际上 Android应用程序窗口UI首先是使用Skia图形库API来绘制在一块画布上,实际地是绘制在这块画布里面的一个图形缓冲区中,这个图形缓冲区最终会被交给SurfaceFlinger服务,而SurfaceFlinger服务再使用OpenGL图形库API来将这个图形缓冲区渲染到硬件帧缓冲区中.

总结

从onCreate中调用setContentView方法开始分析了acitivty界面的绘制显示过程,这样对activity是如何显示一个界面的有了一个大概的了解,我这里只是把我自己的理解记录下来而已,如果看不太懂想详细了解的可以去看 Android应用程序窗口(Activity)实现框架简要介绍和学习计划,我只是在它的基础上,自己梳理一遍思路,让自己理解的更深刻.

阅读全文
0 0