View的绘制流程分析之一


转载请注明出处:http://blog.csdn.net/crazy1235/article/details/71568745



绘制流程从哪里开始的?

Activity具体是怎么创建的?又是怎么显示出来的? 这篇博文中,讲解了从startActivity() 调用之后,到一个activity的显示过程!

activity界面是在onResume()函数回调之后才显示的!

关键函数就在 ActivityThread 中的 handleResumeActivity()

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

        // 这里回调了onResume()生命周期函数,返回ActivityClientRecord对象
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // 如果当前activity的window对象没有被添加到windowManager中,并且没有finish自己,也没有启动其他的activity,那么就把window对象添加到wm中!

            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().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;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                // 将decorview添加到wm中!  (关键代码)
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }

            // 如果window已经被添加到wm中,但是在resume过程中又启动了别的activity,则此时不让window对象显示!
            } else if (!willBeVisible) {
                r.hideForNow = true;
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r, false /* force */);

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                if (r.newConfig != null) {
                    performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                            + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
                    r.newConfig = null;
                }
                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
                        + isForward);
                WindowManager.LayoutParams l = r.window.getAttributes();
                if ((l.softInputMode
                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                        != forwardBit) {
                    l.softInputMode = (l.softInputMode
                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                            | forwardBit;
                     // mVisibleFromClient 默认为true
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l); // update
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                // 让界面显示出来!实际上就是调用了 decorview.setVisiable(VISIABLE)
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }

            // ...
        } else {
            // activity在resume过程中出现异常时,finish这个界面!
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

从上面代码块可以看出,先调用了performResumeActivity(),在这个函数内部毁掉了onResume(),具体分析请查看 onResume()流程分析

接着才是将decorview视图添加到windowManager中!

wm.addView(decor, l);

willBeVisiable 标识是否要显示当前的activity

wm ViewManager 类型对象!

但是ViewManager是个接口


ViewManager

先来看看wm这个对象的真正实现者!

ViewManager wm = a.getWindowManager();

vm的创建时通过 getWindowManager() 来的。而a是Activity对象!

在Activity.java中

public WindowManager getWindowManager() {
        return mWindowManager;
    }

直接返回的是mWindowManager,那么这个对象在Activity中是什么时候创建的呢?

其实就是在 attach() 这个函数中

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        // ...

        mWindow = new PhoneWindow(this, window);
        // ...

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        // ...
    }

attach() 这个函数的调用其实在 ActivityThread类中的 performLaunchActivity() 就已经完成了! 具体流程可以参考:ActivityThread # handleLaunchActivity

在这个attach() 函数中,通过调用PhoneWindow的 getWindowManager() 进行对 mWindowManager 赋值 ~!

再去PhoneWindow.java类中去找一下

public WindowManager getWindowManager() {
        return mWindowManager;
    }

也是直接获取的对象!但是这个对象哪里来的呢????

如果仔细看attach() 方法,就会发现 mWindow.setWindowManager() 这一行代码!

对应到 PhoneWindow

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        // 初始化mWindowManager
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

又跑到了 WindowManagerImpl 类中:

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

OK,至此发现最终是new了一个 WindowManagerImpl 对象!

所以 wm.addView(decor, l); 最终就是调用的 windowManagerImpl.addView(decor, l);

这里写图片描述


addView()

WindowManagerImpl.java

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

其实关于window的操作都是同构WindowManagerGlobal来完成的!

具体的分析请参考 Android对Window对象的管理机制分析

直接来看 WindowManagerGloabl.addView()

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        // ...

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        // 
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {    
            // ...

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

            view.setLayoutParams(wparams);

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


        try {
            // ... 
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // ...
        }
    }

在这个函数内部创建了 ViewRootImpl 对象!它是连接WindowManager与DecorView的桥梁!

最后调用了 ViewRootImpl.setView() 函数!

来看这个方法体

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;

                // ...

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();

                // ... 
        }
    }
}

requestLayout()

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            // ...
        }
    }

mTraversalRunnable 是一个Runnable对象。mChoreographer.postCallback(Runnable)看起来很像Handler的处理机制对吧!其实Choreographer 内部就是通过Handler来处理的!

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
void doTraversal() {
        if (mTraversalScheduled) {
            // ...
            performTraversals();

            // ...
        }
    }

终于找到了 performTraversals() 这个函数了!

其实View的绘制流程就是从这个函数开始的!


MeasureSpec

在开始真正的分析绘制流程之前,先来看看 MeasureSpec 这个类 – 测量规格

MeasureSpec 参与了View的measure过程。

MeasureSpec 是View的一个静态内部类

private static final int MODE_SHIFT = 30;
private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY     = 1 << MODE_SHIFT;
public static final int AT_MOST     = 2 << MODE_SHIFT;

@IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
@Retention(RetentionPolicy.SOURCE)
public @interface MeasureSpecMode {}

public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size, @MeasureSpecMode int mode) {
      if (sUseBrokenMakeMeasureSpec) {
          return size + mode;
      } else {
          return (size & ~MODE_MASK) | (mode & MODE_MASK);
      }
}

@MeasureSpecMode
public static int getMode(int measureSpec) {
       //noinspection ResourceType
       return (measureSpec & MODE_MASK);
}

public static int getSize(int measureSpec) {
       return (measureSpec & ~MODE_MASK);
}

MeasureSpec代表一个32位的int值,高2位表示SpecMode – 测量模式, 低30位表示SpecSize – 测量大小 !

SpecMode与SpecSize组装成MeasureSpec,和,MeasureSpec提取出SpecMode和SpecSize都是通过位运算,非运算,反运算的组合运算得出的!


SpecMode

SpecMode有三种:

  • UNSPECIFIED

    未详细说明的。父容器不对View有限制,一般用于系统内部。自定义控件时基本用不到!

  • EXACTLY

    精确模式。父容器已经确定了子View的大小,所以子View的大小就是SpecSize对应的值。对应于LayoutParams的match_parent和具体的数值这两种方式!

  • AT_MOST

    最多模式。由父容器指定子View的最大宽高,子View的宽高不能超过SpecSize这个值!对应于LayoutParams的wrap_content 方式!


对于顶级decorView来说,MeasureSpec是由窗口尺寸和自身的LayoutParams共同决定!

对于其他普通View来说,MeasureSpec是由父容器的MeasureSpec和自身的LayoutParams共同决定的!


在 performTraversals() 函数里面有这样一段代码:

int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

前两行代码就是构建根布局decorView的宽高测量规格。

lp实际上就是我们wm.addView(decor, l) 传进来的WindowManager.LayoutParams 对象!

getRootMeasureSpec()

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }

从上面代码可看出DecorView的MeasureSpec的构建过程是根据LayoutParams来进行区分的!

  • ViewGroup.LayoutParams.MATCH_PARENT

    精确模式(EXACTLY),大小就是窗口的大小

  • ViewGroup.LayoutParams.WRAP_CONTENT

    最大模式(AT_MOST),最大不超过窗口的大小

  • 具体数值

    精确模式(EXACTLY),大小为rootDimension,也就是WindowManager.LayoutParams中的大小


getChildMeasureSpec()

对于普通的View来说呢?

看下ViewGroup中的 getChildMeasureSpec() 函数,获得子View的测量规格!

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec); // mode
        int specSize = MeasureSpec.getSize(spec); // size

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) { // 判断当前ViewGroup的mode
        case MeasureSpec.EXACTLY: // exactly
            if (childDimension >= 0) { // 具体的数值
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) { // 占满ViewGroup的空间

                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) { // 最多不能超过ViewGoup的大小

                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // ViewGoup是AT_MOST模式的时候
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // 具体数值
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 子View的大小就是ViewGroup的大小
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // 子View的大小最多是ViewGroup的大小
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // ViewGroup大小不确定
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // 具体数值
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 填满ViewGoup的大小
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // 最多不超过ViewGroup的大小
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        // 构造测量模式~
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

总结如下:

  • 普通View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定!

  • 当子View的宽/高采用match_parent时,如果父容器的SpecMode是EXACTLY,则子View的SpecMode也是EXACTLY且大小是父容器的剩余空间(除去padding,margining);如果父容器的SpecMode是AT_MOST模式,则子View的SpecMode也是AT_MOST模式且大小不超过父容器的剩余空间大小。

  • 当子View的宽/高采用wrap_content时,不管父容器的SpecMode是EXACTLY还是AT_MOST,子View的SpecMode都是AT_MOST并且大小不能超过父容器的剩余空间!

  • 当子View的宽/高设定了具体的数值,不管父容器的SpecMOde是什么模式,子View的SpecMode都是EXACTLY并且大小就是LayoutParams中设置的大小!


下面就具体来分析下测量 - 布局 - 绘制这三个流程!

View的绘制流程分析之二 – measure

View的绘制流程分析之三 – layout

View的绘制流程分析之四 – draw


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值