android 布局绘制流程解析

来源:互联网 发布:如何购买备案好的域名 编辑:程序博客网 时间:2024/06/05 23:42

通过上一篇《布局加载流程》中知道了布局的加载;
大家都知道Activity在Android体系中扮演者一个界面展示的角色,通过上一篇文章的分析,我们知道Activity是通过Window来控制界面的展示的,一个Window对象就是一个窗口对象,而每个Activity中都有一个相应的Window对象,所以说一个Activity对象也就可以说是一个窗口对象,而Window只是控制着界面布局文件的加载过程,那么界面布局文件的绘制流程是如何的呢?这篇文章主要就是顺着上篇文章的思路,看一下在android系统中Activity的布局文件是如何绘制的。

android中所有能显示的东西都是通过Window对象实现了,无论Activity,Dialog,PopupWindow,Toast等。后期我可能也会讲一下Dialog,PopupWindow,Toast等组件的显示过程。

在执行ActivityThread的handleLauncherActivity方法中通过Window对象控制了布局文件的加载流程,而Android体系在执行Activity的onResume方法之前会回调ActivityThread的handleResumeActivity方法:

final void handleResumeActivity(IBinder token,            boolean clearHide, boolean isForward, boolean reallyResume) {            ...            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 (a.mVisibleFromClient) {                    a.mWindowAdded = true;                    wm.addView(decor, l);                }            // If the window has already been added, but during resume            // we started another activity, then don't yet make the            // window visible.            }            ...            // 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) {                    r.tmpConfig.setTo(r.newConfig);                    if (r.overrideConfig != null) {                        r.tmpConfig.updateFrom(r.overrideConfig);                    }                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "                            + r.activityInfo.name + " with newConfig " + r.tmpConfig);                    performConfigurationChanged(r.activity, r.tmpConfig);                    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));                    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;                    if (r.activity.mVisibleFromClient) {                        ViewManager wm = a.getWindowManager();                        View decor = r.window.getDecorView();                        wm.updateViewLayout(decor, l);                    }                }                r.activity.mVisibleFromServer = true;                mNumVisibleActivities++;                if (r.activity.mVisibleFromClient) {                    r.activity.makeVisible();                }            }            if (!r.onlyLocalRequest) {                r.nextIdle = mNewActivities;                mNewActivities = r;                if (localLOGV) Slog.v(                    TAG, "Scheduling idle handler for " + r);                Looper.myQueue().addIdleHandler(new Idler());            }            r.onlyLocalRequest = false;            // Tell the activity manager we have resumed.            if (reallyResume) {                try {                    ActivityManagerNative.getDefault().activityResumed(token);                } catch (RemoteException ex) {                }            }            ...    }

可以看到在在获取了Activity的Window相关参数之后执行了r.activity.makeVisible()方法,看样子这个就是Activity的显示方法,这里我们来具体看一下makeVisible方法的具体实现逻辑:

void makeVisible() {        if (!mWindowAdded) {            ViewManager wm = getWindowManager();            wm.addView(mDecor, getWindow().getAttributes());            mWindowAdded = true;        }        mDecor.setVisibility(View.VISIBLE);    }

首先判断成员变量mWindowAdded是否为true,可以发现mWindowAdded成员变量只有在执行之后才能赋值为true,所以这里的代码的主要逻辑是该if分支只能执行一次。

这里的ViewManager对象是通过getWindowManager()方法获取的,我们来看一下getWindowManager()方法的具体实现:

public WindowManager getWindowManager() {        return mWindowManager;    }

好吧,原来就是返回的Activity的mWindowManager的成员变量,那么这个mWindowManager的成员变量是什么时候赋值的呢?上一篇文章我们在Activity的attach方法方法中初始化了Activity的相关成员变量,这里也包括了mWindowManager,我们来看一下mWindowManager的赋值过程:

mWindowManager = mWindow.getWindowManager();

好吧,这里的Window.getWindowManager()方法是具体如何实现的呢?

public WindowManager getWindowManager() {        return mWindowManager;    }

那么这里的Window对象的mWindowManager成员变量是具体如何赋值的?

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 = ((WindowManagerImpl)wm).createLocalWindowManager(this);    }

好吧,可以发现mWindowManager = ((WindowManagerImpl)vm).createLocalWindowManager(this)原来是在这里赋值的,所以一个Activity对应这一个新的Window,而这个Window对象内部会对应着一个新的WindowManager对象,我们接着往下看,那么createLoclWindowManager方法是如何实现的呢?

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

好吧,原来是new出了一个WindowManagerImpl对象,所以回到我们的Activity的makeVisible方法,ViewManager获取的是一个WindowManagerImpl对象,所以Window对象内部的WindowManager对象其实都是一个WindowManagerImpl的实例,都是而且从继承关系上可以看到:

WindowManagerImpl –> WindowManager –> ViewManager;

继续往下看:

wm.addView(mDecor, getWindow().getAttributes());

这里的mDector成员变量,通过上一篇文章的介绍,我们知道,它是Activity的界面根View,而getWindow.getAttrbutes方法是windowManager中定义的Params内部类,该内部类定义了许多的Window类型,由于这里的vm是WindowManagerImpl的实例,我们来看一下这里的addView的具体实现:

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

然后我们具体看一下mGlobal.addView方法,这里的mGlobal是一个WindowManagerGlobal的单例对象,WindowManagerGlobal是Window处理的工具类,那么WindowManagerGlobal的addView具体是如何实现的呢?

public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {        ...        ViewRootImpl root;        View panelParentView = null;        synchronized (mLock) {            // Start watching for system property changes.            if (mSystemPropertyUpdater == null) {                mSystemPropertyUpdater = new Runnable() {                    @Override public void run() {                        synchronized (mLock) {                            for (int i = mRoots.size() - 1; i >= 0; --i) {                                mRoots.get(i).loadSystemProperties();                            }                        }                    }                };                SystemProperties.addChangeCallback(mSystemPropertyUpdater);            }            int index = findViewLocked(view, false);            if (index >= 0) {                if (mDyingViews.contains(view)) {                    // Don't wait for MSG_DIE to make it's way through root's queue.                    mRoots.get(index).doDie();                } else {                    throw new IllegalStateException("View " + view                            + " has already been added to the window manager.");                }                // The previous removeView() had not completed executing. Now it has.            }            // If this is a panel window, then find the window it is being            // attached to for future reference.            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {                final int count = mViews.size();                for (int i = 0; i < count; i++) {                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {                        panelParentView = mViews.get(i);                    }                }            }            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.            synchronized (mLock) {                final int index = findViewLocked(view, false);                if (index >= 0) {                    removeViewLocked(index, true);                }            }            throw e;        }    }

可以发现在WindowManagerGlobal中存在着三个数据列表:

private final ArrayList<View> mViews = new ArrayList<View>();private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();private final ArrayList<WindowManager.LayoutParams> mParams =            new ArrayList<WindowManager.LayoutParams>();

其中mViews主要用于保存Activity的mDector也就是Activity的根View,而mRoots主要用于保存ViewRootImpl,mParams主要用于保存Window的LayoutParams,WindowManagerGlobal主要作为WindowManagerImpl的辅助方法类,用于操作View组件。

最后我们调用了root.setView方法,这个方法很重要我们就是在这里实现了我们的root与ViewRootImpl的关联的,除了实现了mDector与ViewRootImpl的相互关联,我们还调用了requestLayout方法,这里我们看一下setView方法的具体实现:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        ...        requestLayout();        ...    }

可以看到,在方法体中又调用了requestLayout方法,这个方法其实就是调用执行重绘的请求,我们来看一下这个requestLayout方法具体实现:

@Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            checkThread();            mLayoutRequested = true;            scheduleTraversals();        }    }

可以看到这里有一个checkThread方法,这个方法是检查当前线程的方法,若当前线程非UI线程,则抛出非UI线程更新UI的错误:

void checkThread() {        if (mThread != Thread.currentThread()) {            throw new CalledFromWrongThreadException(                    "Only the original thread that created a view hierarchy can touch its views.");        }    }

相信大家平时在编程的过程中肯定会遇到过这个错误,ViewRootImpl是具体更新View的管理类,所有关于View的更新操作都是在这里执行的,自然而然的对于更新线程的检测是在这个类中添加的,一般在更新UI的时候都会调用这个方法用于检测当前执行更新UI的线程是否是UI线程,否则就会抛出这个异常。

继续回到我们的requestLayout方法,这里又调用了scheduleTraversales方法,我们来看一下这个方法的具体实现:

void scheduleTraversals() {        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            if (!mUnbufferedInputDispatch) {                scheduleConsumeBatchedInput();            }            notifyRendererOfFramePending();            pokeDrawLockIfNeeded();        }    }

这里mChoreographer.postCallback,内部会调用一个异步消息,用于执行mTraversalRunnable的run方法,这个mTraversalRunnable是一个Runnable对象,我们来看一下mTraversalRunnable类的定义:

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

在TraversalRunnable类的run方法中调用了doTraversal方法,我们来看一下这个方法的具体实现逻辑:

void doTraversal() {        if (mTraversalScheduled) {            mTraversalScheduled = false;            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);            if (mProfile) {                Debug.startMethodTracing("ViewAncestor");            }            performTraversals();            if (mProfile) {                Debug.stopMethodTracing();                mProfile = false;            }        }    }

好吧,其内部又回调了方法performTraversals方法,这个方法就是整个View的绘制起始方法,从这个方法开始我们的View经过大小测量,位置测量,界面绘制三个逻辑操作之后就可以展示在界面中了。

private void performTraversals() {        ...        // 执行View组件的onMeasure方法,主要用于测量View            if (!mStopped || mReportNextDraw) {                boolean focusChangedDueToTouchMode = ensureTouchModeLocally(                        (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);                    if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed!  mWidth="                            + mWidth + " measuredWidth=" + host.getMeasuredWidth()                            + " mHeight=" + mHeight                            + " measuredHeight=" + host.getMeasuredHeight()                            + " coveredInsetsChanged=" + contentInsetsChanged);                     // Ask host how big it wants to be                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                    // Implementation of weights from WindowManager.LayoutParams                    // We just grow the dimensions as needed and re-measure if                    // needs be                    int width = host.getMeasuredWidth();                    int height = host.getMeasuredHeight();                    boolean measureAgain = false;                    if (lp.horizontalWeight > 0.0f) {                        width += (int) ((mWidth - width) * lp.horizontalWeight);                        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,                                MeasureSpec.EXACTLY);                        measureAgain = true;                    }                    if (lp.verticalWeight > 0.0f) {                        height += (int) ((mHeight - height) * lp.verticalWeight);                        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,                                MeasureSpec.EXACTLY);                        measureAgain = true;                    }                    if (measureAgain) {                        if (DEBUG_LAYOUT) Log.v(TAG,                                "And hey let's measure once more: width=" + width                                + " height=" + height);                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                    }                    layoutRequested = true;                }            }        }        ...        // 主要用于测量View组件的位置        ...        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);        boolean triggerGlobalLayoutListener = didLayout                || mAttachInfo.mRecomputeGlobalAttributes;        if (didLayout) {            performLayout(lp, desiredWindowWidth, desiredWindowHeight);            // By this point all views have been sized and positioned            // We can compute the transparent area            if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {                // start out transparent                // TODO: AVOID THAT CALL BY CACHING THE RESULT?                host.getLocationInWindow(mTmpLocation);                mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],                        mTmpLocation[0] + host.mRight - host.mLeft,                        mTmpLocation[1] + host.mBottom - host.mTop);                host.gatherTransparentRegion(mTransparentRegion);                if (mTranslator != null) {                    mTranslator.translateRegionInWindowToScreen(mTransparentRegion);                }                if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {                    mPreviousTransparentRegion.set(mTransparentRegion);                    mFullRedrawNeeded = true;                    // reconfigure window manager                    try {                        mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);                    } catch (RemoteException e) {                    }                }            }            if (DBG) {                System.out.println("======================================");                System.out.println("performTraversals -- after setFrame");                host.debug();            }        }        ...        // 主要用于View的绘制过程        ...        if (!cancelDraw && !newSurface) {            if (!skipDraw || mReportNextDraw) {                if (mPendingTransitions != null && mPendingTransitions.size() > 0) {                    for (int i = 0; i < mPendingTransitions.size(); ++i) {                        mPendingTransitions.get(i).startChangingAnimations();                    }                    mPendingTransitions.clear();                }                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();            }        }        mIsInTraversal = false;    }

可以看到在方法performTraversals方法,我们调用了performMeasure,performLayout,performDraw三个方法,这几个方法主要用于测量View组件的大小,测量View组件的位置,绘制View组件;

即:测量大小 –> 测量位置 –> 绘制组件

好吧,这里我们调用了performMeasure方法,我们先看一下performMeasure方法的具体实现:

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");        try {            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);        } finally {            Trace.traceEnd(Trace.TRACE_TAG_VIEW);        }    }

可以看到在performMeasure方法中我们又调用了mView的measure方法,这里的mView就是我们一开始的Activity的mDector根组件,这里的measure方法就是调用的mDector组件的measure方法:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {        ...        onMeasure(widthMeasureSpec, heightMeasureSpec);        ...    }

在View的measure方法中,又调用了onMeasure方法,由于我们的mDector对象是一个FrameLayout,所以这里的onMeasure执行的是FrameLayout的onMeasure方法:

@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int count = getChildCount();        final boolean measureMatchParentChildren =                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;        mMatchParentChildren.clear();        int maxHeight = 0;        int maxWidth = 0;        int childState = 0;        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (mMeasureAllChildren || child.getVisibility() != GONE) {                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                maxWidth = Math.max(maxWidth,                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);                maxHeight = Math.max(maxHeight,                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);                childState = combineMeasuredStates(childState, child.getMeasuredState());                if (measureMatchParentChildren) {                    if (lp.width == LayoutParams.MATCH_PARENT ||                            lp.height == LayoutParams.MATCH_PARENT) {                        mMatchParentChildren.add(child);                    }                }            }        }        // Account for padding too        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();        // Check against our minimum height and width        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());        // Check against our foreground's minimum height and width        final Drawable drawable = getForeground();        if (drawable != null) {            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());        }        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),                resolveSizeAndState(maxHeight, heightMeasureSpec,                        childState << MEASURED_HEIGHT_STATE_SHIFT));        count = mMatchParentChildren.size();        if (count > 1) {            for (int i = 0; i < count; i++) {                final View child = mMatchParentChildren.get(i);                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();                final int childWidthMeasureSpec;                if (lp.width == LayoutParams.MATCH_PARENT) {                    final int width = Math.max(0, getMeasuredWidth()                            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()                            - lp.leftMargin - lp.rightMargin);                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(                            width, MeasureSpec.EXACTLY);                } else {                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +                            lp.leftMargin + lp.rightMargin,                            lp.width);                }                final int childHeightMeasureSpec;                if (lp.height == LayoutParams.MATCH_PARENT) {                    final int height = Math.max(0, getMeasuredHeight()                            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()                            - lp.topMargin - lp.bottomMargin);                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(                            height, MeasureSpec.EXACTLY);                } else {                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +                            lp.topMargin + lp.bottomMargin,                            lp.height);                }                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);            }        }    }

可以看到这里调用了一个循环逻辑,获取该View的所有子View,并执行所有子View的measure方法,这样又回到View的measure方法,这样经过一系列的循环遍历过程,如果是ViewGroup就会调用其ViewGroup的onMeasure方法,若果是View组件就会调用View的onMeasure方法,我们来看一下View的onMeasure方法:

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

可以看到这个方法中调用了setMeasuredDimension方法:

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {        boolean optical = isLayoutModeOptical(this);        if (optical != isLayoutModeOptical(mParent)) {            Insets insets = getOpticalInsets();            int opticalWidth  = insets.left + insets.right;            int opticalHeight = insets.top  + insets.bottom;            measuredWidth  += optical ? opticalWidth  : -opticalWidth;            measuredHeight += optical ? opticalHeight : -opticalHeight;        }        setMeasuredDimensionRaw(measuredWidth, measuredHeight);    }

好吧,方法体里面又调用了setMeasuredDimensionRaw方法:

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {        mMeasuredWidth = measuredWidth;        mMeasuredHeight = measuredHeight;        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;    }

这样把View组件即其子View的大小测量出来了,并且保存在了成员变量mMeasuredWith和mMeasuredHeight中。

继续回到我们的performTransles方法,然后我们继续看performLayout方法:

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,            int desiredWindowHeight) {        mLayoutRequested = false;        mScrollMayChange = true;        mInLayout = true;        final View host = mView;        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {            Log.v(TAG, "Laying out " + host + " to (" +                    host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");        }        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");        try {            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());            mInLayout = false;            int numViewsRequestingLayout = mLayoutRequesters.size();            if (numViewsRequestingLayout > 0) {                // requestLayout() was called during layout.                // If no layout-request flags are set on the requesting views, there is no problem.                // If some requests are still pending, then we need to clear those flags and do                // a full request/measure/layout pass to handle this situation.                ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,                        false);                if (validLayoutRequesters != null) {                    // Set this flag to indicate that any further requests are happening during                    // the second pass, which may result in posting those requests to the next                    // frame instead                    mHandlingLayoutInLayoutRequest = true;                    // Process fresh layout requests, then measure and layout                    int numValidRequests = validLayoutRequesters.size();                    for (int i = 0; i < numValidRequests; ++i) {                        final View view = validLayoutRequesters.get(i);                        Log.w("View", "requestLayout() improperly called by " + view +                                " during layout: running second layout pass");                        view.requestLayout();                    }                    measureHierarchy(host, lp, mView.getContext().getResources(),                            desiredWindowWidth, desiredWindowHeight);                    mInLayout = true;                    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());                    mHandlingLayoutInLayoutRequest = false;                    // Check the valid requests again, this time without checking/clearing the                    // layout flags, since requests happening during the second pass get noop'd                    validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);                    if (validLayoutRequesters != null) {                        final ArrayList<View> finalRequesters = validLayoutRequesters;                        // Post second-pass requests to the next frame                        getRunQueue().post(new Runnable() {                            @Override                            public void run() {                                int numValidRequests = finalRequesters.size();                                for (int i = 0; i < numValidRequests; ++i) {                                    final View view = finalRequesters.get(i);                                    Log.w("View", "requestLayout() improperly called by " + view +                                            " during second layout pass: posting in next frame");                                    view.requestLayout();                                }                            }                        });                    }                }            }        } finally {            Trace.traceEnd(Trace.TRACE_TAG_VIEW);        }        mInLayout = false;    }

可以看到在方法体中,我们看到该方法执行了layout方法,我们看一下该layout方法的实现:

public void layout(int l, int t, int r, int b) {        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;        }        int oldL = mLeft;        int oldT = mTop;        int oldB = mBottom;        int oldR = mRight;        boolean changed = isLayoutModeOptical(mParent) ?                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {            onLayout(changed, l, t, r, b);            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;            ListenerInfo li = mListenerInfo;            if (li != null && li.mOnLayoutChangeListeners != null) {                ArrayList<OnLayoutChangeListener> listenersCopy =                        (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();                int numListeners = listenersCopy.size();                for (int i = 0; i < numListeners; ++i) {                    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);                }            }        }        mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;    }

可以看到这个方法体中执行了onLayout方法,这个方法就是具体执行测量位置的方法了,由于我们的mDector是一个FrameLayout,所以跟measure类似的,我们看一下FrameLayout的onLayout方法的实现:

我们看到我们定义了一个循环逻辑,获取所有的validLayoutRequesters也就是需要执行Layout方法的View的集合,通过循环执行view的requestLayout方法。这里我们来看一下requestLayout方法的具体实现:

@Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        layoutChildren(left, top, right, bottom, false /* no force left gravity */);    }

可以看到这里调用了layoutChildren方法,让我们来看一下layoutChildren方法的实现:

void layoutChildren(int left, int top, int right, int bottom,                                  boolean forceLeftGravity) {        final int count = getChildCount();        final int parentLeft = getPaddingLeftWithForeground();        final int parentRight = right - left - getPaddingRightWithForeground();        final int parentTop = getPaddingTopWithForeground();        final int parentBottom = bottom - top - getPaddingBottomWithForeground();        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                final int width = child.getMeasuredWidth();                final int height = child.getMeasuredHeight();                int childLeft;                int childTop;                int gravity = lp.gravity;                if (gravity == -1) {                    gravity = DEFAULT_CHILD_GRAVITY;                }                final int layoutDirection = getLayoutDirection();                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {                    case Gravity.CENTER_HORIZONTAL:                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +                        lp.leftMargin - lp.rightMargin;                        break;                    case Gravity.RIGHT:                        if (!forceLeftGravity) {                            childLeft = parentRight - width - lp.rightMargin;                            break;                        }                    case Gravity.LEFT:                    default:                        childLeft = parentLeft + lp.leftMargin;                }                switch (verticalGravity) {                    case Gravity.TOP:                        childTop = parentTop + lp.topMargin;                        break;                    case Gravity.CENTER_VERTICAL:                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +                        lp.topMargin - lp.bottomMargin;                        break;                    case Gravity.BOTTOM:                        childTop = parentBottom - height - lp.bottomMargin;                        break;                    default:                        childTop = parentTop + lp.topMargin;                }                child.layout(childLeft, childTop, childLeft + width, childTop + height);            }        }    }

跟measure类似的,这里也是遍历执行View的layout方法,若是ViewGroup则执行具体的ViewGroup的layout方法,若是View,则执行View的layout方法,好吧,我们看一下View的layout的具体实现逻辑:

public void layout(int l, int t, int r, int b) {        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;        }        int oldL = mLeft;        int oldT = mTop;        int oldB = mBottom;        int oldR = mRight;        boolean changed = isLayoutModeOptical(mParent) ?                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {            onLayout(changed, l, t, r, b);            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;            ListenerInfo li = mListenerInfo;            if (li != null && li.mOnLayoutChangeListeners != null) {                ArrayList<OnLayoutChangeListener> listenersCopy =                        (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();                int numListeners = listenersCopy.size();                for (int i = 0; i < numListeners; ++i) {                    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);                }            }        }        mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;    }

这样经过layout方法,如果是View组件的话就已经将View组件的位置信息计算出来并保存在对象的成员变量中。

好吧,经过了测量大小与测量位置的逻辑之后,我们最后看一下performTraversals方法中的performDraw方法,这个方法的作用就是执行View组件的绘制逻辑了。

private void performDraw() {        ...        draw(fullRedrawNeeded);        ...    }

可以看到这里调用了ViewRootImpl的draw方法,然后我们看一下draw方法的实现:

private void draw(boolean fullRedrawNeeded) {            ...            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {               return;            }            ...    }

可以看到这里又调用了drawSoftware方法,看名字这里应该就是调用执行绘制的方法:

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,            boolean scalingRequired, Rect dirty) {        ...        mView.draw(canvas);        ...        return true;    }

可以看到这里调用了mView的draw方法,这里的mView是我们的mDector,好吧,看一下draw方法的具体实现:

public void draw(Canvas canvas) {        final int privateFlags = mPrivateFlags;        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);        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;        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;        }        /*         * Here we do the full fledged routine...         * (this is an uncommon case where speed matters less,         * this is why we repeat some of the tests that have been         * done above)         */        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);    }

整个View的绘制流程还是比较清楚的,整个执行逻辑还有相应的注释,一共大概需要六步,并且在执行draw方法的过程中,如果包含子View,那么也会执行子View的draw方法,好吧,经过这样一系列的执行逻辑之后,mDector以及子View就被绘制出来了。

总结

Activity执行onResume之后再ActivityThread中执行Activity的makeVisible方法。

View的绘制流程包含了测量大小,测量位置,绘制三个流程;

Activty的界面绘制是从mDector即根View开始的,也就是从mDector的测量大小,测量位置,绘制三个流程;

View体系的绘制流程是从ViewRootImpl的performTraversals方法开始的;

View的测量大小流程:performMeasure –> measure –> onMeasure等方法;

View的测量位置流程:performLayout –> layout –> onLayout等方法;

View的绘制流程:onDraw等方法;

View组件的绘制流程会在onMeasure,onLayout以及onDraw方法中执行分发逻辑,也就是在onMeasure同时执行子View的测量大小逻辑,在onLayout中同时执行子View的测量位置逻辑,在onDraw中同时执行子View的绘制逻辑;

Activity中都对应这个一个Window对象,而每一个Window对象都对应着一个新的WindowManager对象(WindowManagerImpl实例);

原创粉丝点击