Android View绘制流程源码浅析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tianmi1988/article/details/50210751

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, well
2977        // we are back active so skip it.
2978        unscheduleGcIdler();
2979        mSomeActivitiesChanged = true;
2980
2981        // TODO Push resumeArgs into the activity for consideration
2982        ActivityClientRecord r = performResumeActivity(token, clearHide);
2983
2984        if (r != null) {
2985            final Activity a = r.activity;
2986
2987            if (localLOGV) Slog.v(
2988                TAG, "Resume " + r + " started activity: " +
2989                a.mStartedActivity + ", hideForNow: " + r.hideForNow
2990                + ", finished: " + a.mFinished);
2991
2992            final int forwardBit = isForward ?
2993                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
2994
2995            // 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                }
3019
3020            // If the window has already been added, but during resume
3021            // we started another activity, then don't yet make the
3022            // window visible.
3023            } else if (!willBeVisible) {
3024                if (localLOGV) Slog.v(
3025                    TAG, "Launch " + r + " mStartedActivity set");
3026                r.hideForNow = true;
3027            }
3028
3029            // Get rid of anything left hanging around.
3030            cleanUpPendingRemoveWindows(r);
3031
3032            // The window is now visible if it has been added, we are not
3033            // simply finishing, and we are not starting another activity.
3034            if (!r.activity.mFinished && willBeVisible
3035                    && 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.softInputMode
3047                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
3048                        != forwardBit) {
3049                    l.softInputMode = (l.softInputMode
3050                            & (~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            }
3064
3065            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;
3073
3074            // Tell the activity manager we have resumed.
3075            if (reallyResume) {
3076                try {
3077                    ActivityManagerNative.getDefault().activityResumed(token);
3078                } catch (RemoteException ex) {
3079                }
3080            }
3081
3082        } else {
3083            // If an exception was thrown when trying to resume, then
3084            // 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,也就是为何根视图总是全屏的原因。




猜你喜欢

转载自blog.csdn.net/tianmi1988/article/details/50210751