Android View绘制流程源码浅析

来源:互联网 发布:数据库分区和分表 编辑:程序博客网 时间:2024/06/05 03:23

Android中View的绘制是一个面试的必答题,网上他人的博文也很多,本文旨在分析出大致流程。

废话不说,read the fucking source code!

先从ActivityThread主线程启动Activity说起,当Activity初始化 Window和将布局添加到

PhoneWindow的内部类DecorView类之后,ActivityThread类会调用handleResumeActivity方法将顶层视图DecorView添加到PhoneWindow窗口,来看看handlerResumeActivity方法的实现:

2974    final void handleResumeActivity(IBinder token,2975            boolean clearHide, boolean isForward, boolean reallyResume) {2976        // If we are getting ready to gc after going to the background, well2977        // we are back active so skip it.2978        unscheduleGcIdler();2979        mSomeActivitiesChanged = true;29802981        // TODO Push resumeArgs into the activity for consideration2982        ActivityClientRecord r = performResumeActivity(token, clearHide);29832984        if (r != null) {2985            final Activity a = r.activity;29862987            if (localLOGV) Slog.v(2988                TAG, "Resume " + r + " started activity: " +2989                a.mStartedActivity + ", hideForNow: " + r.hideForNow2990                + ", finished: " + a.mFinished);29912992            final int forwardBit = isForward ?2993                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;29942995            // If the window hasn't yet been added to the window manager,2996            // and this guy didn't finish itself or start another activity,2997            // then go ahead and add the window.2998            boolean willBeVisible = !a.mStartedActivity;2999            if (!willBeVisible) {3000                try {3001                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(3002                            a.getActivityToken());3003                } catch (RemoteException e) {3004                }3005            }3006            if (r.window == null && !a.mFinished && willBeVisible) {3007                r.window = r.activity.getWindow();3008                View decor = r.window.getDecorView();3009                decor.setVisibility(View.INVISIBLE);3010                ViewManager wm = a.getWindowManager();3011                WindowManager.LayoutParams l = r.window.getAttributes();3012                a.mDecor = decor;3013                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;3014                l.softInputMode |= forwardBit;3015                if (a.mVisibleFromClient) {3016                    a.mWindowAdded = true;3017                    wm.addView(decor, l);3018                }30193020            // If the window has already been added, but during resume3021            // we started another activity, then don't yet make the3022            // window visible.3023            } else if (!willBeVisible) {3024                if (localLOGV) Slog.v(3025                    TAG, "Launch " + r + " mStartedActivity set");3026                r.hideForNow = true;3027            }30283029            // Get rid of anything left hanging around.3030            cleanUpPendingRemoveWindows(r);30313032            // The window is now visible if it has been added, we are not3033            // simply finishing, and we are not starting another activity.3034            if (!r.activity.mFinished && willBeVisible3035                    && r.activity.mDecor != null && !r.hideForNow) {3036                if (r.newConfig != null) {3037                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "3038                            + r.activityInfo.name + " with newConfig " + r.newConfig);3039                    performConfigurationChanged(r.activity, r.newConfig);3040                    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.newConfig));3041                    r.newConfig = null;3042                }3043                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="3044                        + isForward);3045                WindowManager.LayoutParams l = r.window.getAttributes();3046                if ((l.softInputMode3047                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)3048                        != forwardBit) {3049                    l.softInputMode = (l.softInputMode3050                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))3051                            | forwardBit;3052                    if (r.activity.mVisibleFromClient) {3053                        ViewManager wm = a.getWindowManager();3054                        View decor = r.window.getDecorView();3055                        wm.updateViewLayout(decor, l);3056                    }3057                }3058                r.activity.mVisibleFromServer = true;3059                mNumVisibleActivities++;3060                if (r.activity.mVisibleFromClient) {3061                    r.activity.makeVisible();3062                }3063            }30643065            if (!r.onlyLocalRequest) {3066                r.nextIdle = mNewActivities;3067                mNewActivities = r;3068                if (localLOGV) Slog.v(3069                    TAG, "Scheduling idle handler for " + r);3070                Looper.myQueue().addIdleHandler(new Idler());3071            }3072            r.onlyLocalRequest = false;30733074            // Tell the activity manager we have resumed.3075            if (reallyResume) {3076                try {3077                    ActivityManagerNative.getDefault().activityResumed(token);3078                } catch (RemoteException ex) {3079                }3080            }30813082        } else {3083            // If an exception was thrown when trying to resume, then3084            // just end this activity.3085            try {3086                ActivityManagerNative.getDefault()3087                    .finishActivity(token, Activity.RESULT_CANCELED, null, false);3088            } catch (RemoteException ex) {3089            }3090        }3091    }

接着看82行也就是源码3055行(基于android5.0)  wm.addView(decor, l);

ViewManager wm= a.getWindowManager();这里有个向上转型,wm的具体对象Activity的WindowManagerImpl对象

接着看 WindowManagerImpl的addView方法

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

这里的mGlobal是个单实例的WindowManagerGlobal类。进入WindowManagerGlobal类看addView()方法。

 public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {        ............        ViewRootImpl root;        View panelParentView = null;        ............        //获得ViewRootImpl对象root         root = new ViewRootImpl(view.getContext(), display);        ...........        // do this last because it fires off messages to start doing things        try {            //将传进来的参数DecorView设置到root中            root.setView(view, wparams, panelParentView);        } catch (RuntimeException e) {          ...........        }    }

创建了一个ViewRootImpl对象root,然后调用ViewRootImpl类中的setView成员方法()。继续跟踪代码进入ViewRootImpl类

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {            if (mView == null) {            //将顶层视图DecorView赋值给全局的mView              mView = view;            .............            //标记已添加DecorView             mAdded = true;            .............            requestLayout();            .............             } }
接着看ViewRootImpl的内部方法调用

    @Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            checkThread();            mLayoutRequested = true;            scheduleTraversals();        }    }    ................void scheduleTraversals() {        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            if (!mUnbufferedInputDispatch) {                scheduleConsumeBatchedInput();            }            notifyRendererOfFramePending();        }    }..............final class TraversalRunnable implements Runnable {        @Override        public void run() {            doTraversal();        }    }final TraversalRunnable mTraversalRunnable = new TraversalRunnable();............... void doTraversal() {        if (mTraversalScheduled) {            mTraversalScheduled = false;            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);            try {                performTraversals();            } finally {                Trace.traceEnd(Trace.TRACE_TAG_VIEW);            }        }    }............

最后DecorView的绘制会进入到ViewRootImpl类中的performTraversals()成员方法。


现在我们主要来分析下 ViewRootImpl类中的performTraversals()方法

private void performTraversals() {        // cache mView since it is used so much below...        //mView就是DecorView根布局        final View host = mView;        //mAdded赋值为true,因此条件不成立        if (host == null || !mAdded)            return;        //是否正在遍历        mIsInTraversal = true;        //是否马上绘制View        mWillDrawSoon = true;        .............        //顶层视图DecorView所需要窗口的宽度和高度        int desiredWindowWidth;        int desiredWindowHeight;        .....................        //在构造方法中mFirst已经设置为true,表示是否是第一次绘制DecorView        if (mFirst) {            mFullRedrawNeeded = true;            mLayoutRequested = true;            //如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要窗口的宽度和高度就是除了状态栏            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {                // NOTE -- system code, won't try to do compat mode.                Point size = new Point();                mDisplay.getRealSize(size);                desiredWindowWidth = size.x;                desiredWindowHeight = size.y;            } else {//否则顶层视图DecorView所需要窗口的宽度和高度就是整个屏幕的宽高                DisplayMetrics packageMetrics =                    mView.getContext().getResources().getDisplayMetrics();                desiredWindowWidth = packageMetrics.widthPixels;                desiredWindowHeight = packageMetrics.heightPixels;            }    }............//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);  // Ask host how big it wants to be  //执行测量操作  performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);........................//执行布局操作 performLayout(lp, desiredWindowWidth, desiredWindowHeight);.......................//执行绘制操作performDraw();}

先看ViewRootImpl#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);        }    }

ViewRootImpl的mView就是DecorView对象

回过头看DecorView的两个参数怎么来的

ViewRootImpl#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;    }

继续回溯第二个参数在performTraversals函数中找到

WindowManager.LayoutParams lp = mWindowAttributes;
ViewRootImpl#setView

/**     * We have one child     */    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {            if (mView == null) {                mView = view;                mAttachInfo.mDisplayState = mDisplay.getState();                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);                mViewLayoutDirectionInitial = mView.getRawLayoutDirection();                mFallbackEventHandler.setView(view);                mWindowAttributes.copyFrom(attrs);                if (mWindowAttributes.packageName == null) {                    mWindowAttributes.packageName = mBasePackageName;                }                attrs = mWindowAttributes;         ......

再向上找setView在handleResumeActivity中被调用,参数l来源

WindowManager.LayoutParams l = r.window.getAttributes();  

继续挖,Window#getAttributes

    public final WindowManager.LayoutParams getAttributes() {        return mWindowAttributes;    }<pre name="code" class="java">    // The current window attributes.    private final WindowManager.LayoutParams mWindowAttributes =        new WindowManager.LayoutParams();

好吧,有个默认的值,答案就要解开了

android.view.WindowManager

        public  LayoutParams() {            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);            type = TYPE_APPLICATION;            format = PixelFormat.OPAQUE;        }
到此为止,终于解开了getRootMeasureSpec传入的第二个参数,默认为MATCH_PARENT。getRootMeasureSpec传入参数后这个函数走的是MATCH_PARENT,使用MeasureSpec.makeMeasureSpec方法组装一个MeasureSpec,MeasureSpec的specMode等于EXACTLY,specSize等于windowSize,也就是为何根视图总是全屏的原因。




0 0
原创粉丝点击