【Android】Measure Layout Draw

在顶级ViewRoot中进行测量、定位、绘制过程。

直白点:

measure,给出view宽高

layout,给出view的left、top、right、bottom → 四个点确定view位置

draw,在给定位置上绘制view

MeasureSpec:

一个32位int(高2位表示mode,低30位表示size)

private static final int MODE_SHIFT = 30;
private static final int MODE_MASK  = 0x3 << MODE_SHIFT;//模,用于解int

//三种测量模式
public static final int UNSPECIFIED = 0 << MODE_SHIFT;//父容器不对view有限制,要多大给多大,
public static final int EXACTLY     = 1 << MODE_SHIFT;//精确模式,父容器已经检测出view所需的精确大小,对应LayoutParams中match_parent属性,

public static final int AT_MOST = 2 << MODE_SHIFT;//父容器制定一个可用大小即SpecSize,View的大小不能大于这个值,对于LayoutParams中wrap_content,


在Activity启动过程中,涉及界面代码在Resume阶段,也就是handleResumeActivity函数中,进入ActivityThread中源码如下:

@Override

    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,

            String reason) {

        // If we are getting ready to gc after going to the background, well

        // we are back active so skip it.

        unscheduleGcIdler();

        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration

        //此处会回调Activity的onResume方法,而视图相关代码还在下面。

        //拿到ActivityRecord关键点在一个token,可以理解为Activity在ActivityThread中保存的令牌,本身是个Binder,因为ActivityThread有个成员变量mActivities,专门用来存储此进程中使用的Activity。

        //具体如何放进去呢?可以参考Activity启动流程。

        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

        if (r == null) {

            // We didn't actually resume the activity, so skipping any follow-up actions.

            return;

        }

        if (mActivitiesToBeDestroyed.containsKey(token)) {

            // Although the activity is resumed, it is going to be destroyed. So the following

            // UI operations are unnecessary and also prevents exception because its token may

            // be gone that window manager cannot recognize it. All necessary cleanup actions

            // performed below will be done while handling destruction.

            return;

        }

        final Activity a = r.activity; //拿到Activity,ActivityRecord是事实上的窗口。

        if (localLOGV) {

            Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity

                    ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);

        }

        final int forwardBit = isForward

                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

        // If the window hasn't yet been added to the window manager,

        // and this guy didn't finish itself or start another activity,

        // then go ahead and add the window.

        boolean willBeVisible = !a.mStartedActivity;

        if (!willBeVisible) {

            try {

                willBeVisible = ActivityTaskManager.getService().willActivityBeVisible(

                        a.getActivityToken());

            catch (RemoteException e) {

                throw e.rethrowFromSystemServer();

            }

        }

         // 此处,就是和视图相关的代码了

        if (r.window == null && !a.mFinished && willBeVisible) {

            r.window = r.activity.getWindow();//拿到window,此处实例是PhoneWindow

            View decor = r.window.getDecorView();// 拿到顶层容器

            decor.setVisibility(View.INVISIBLE); //先设置不可见,在后面makeVisible函数才显示可见

            ViewManager wm = a.getWindowManager();// wm

            WindowManager.LayoutParams l = r.window.getAttributes();//布局参数

            a.mDecor = decor;// Activity 和 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(); // 主角来了,ViewRootImpl!

                if (impl != null) {

                    impl.notifyChildRebuilt();

                }

            }

            if (a.mVisibleFromClient) {

                if (!a.mWindowAdded) {

                    a.mWindowAdded = true;

                    //关键,将decor放入window中!具体实现类在WindowManagerImpl,然后用WindowManagerGlobal全局类委托给ViewRootImpl进行具体三个过程。

                    wm.addView(decor, l);

                else {

                    // The activity will get a callback for this {@link LayoutParams} change

                    // earlier. However, at that time the decor will not be set (this is set

                    // in this method), so no action will be taken. This call ensures the

                    // callback occurs with the decor set.

                    a.onWindowAttributesChanged(l);

                }

            }

            // If the window has already been added, but during resume

            // we started another activity, then don't yet make the

            // window visible.

        else if (!willBeVisible) {

            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");

            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);

                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);

            ViewRootImpl impl = r.window.getDecorView().getViewRootImpl();

            WindowManager.LayoutParams l = impl != null

                    ? impl.mWindowAttributes : 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(); //执行此处,使得Decor对用户可见,但还未立即可见。

            }

        }

        r.nextIdle = mNewActivities;

        mNewActivities = r;

        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);

        Looper.myQueue().addIdleHandler(new Idler());

    }

我们直接来到WindowManagerGlobal的addView中,其它remove和update类似:

public void addView(View view, ViewGroup.LayoutParams params,

           Display display, Window parentWindow, int userId) {

       if (view == null) {

           throw new IllegalArgumentException("view must not be null");

       }

       if (display == null) {

           throw new IllegalArgumentException("display must not be null");

       }

       if (!(params instanceof WindowManager.LayoutParams)) {

           // 检查参数类型

           throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");

       }

       final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

       if (parentWindow != null) {

           parentWindow.adjustLayoutParamsForSubWindow(wparams);

       else {

           // If there's no parent, then hardware acceleration for this view is

           // set from the application's hardware acceleration setting.

           final Context context = view.getContext();

           if (context != null

                   && (context.getApplicationInfo().flags

                           & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {

               wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;

           }

       }

       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); // new了一个VIewRootImpl,

           view.setLayoutParams(wparams);

           mViews.add(view); //添加到全局视图中保存,这三个列表序列保持一致,对应一个View,如果使用ViewRecord类封装是不是好一点?

           mRoots.add(root);

           mParams.add(wparams);

           // do this last because it fires off messages to start doing things

           try {

               root.setView(view, wparams, panelParentView, userId); //三大过程的入口!

           catch (RuntimeException e) {

               // BadTokenException or InvalidDisplayException, clean up.

               throw e;

           }

       }

   }

在root.setView函数里面,调用了一个方法,requestLayout,顾名思义就是请求布局,而该函数实现如下:

@Override

    public void requestLayout() {

        if (!mHandlingLayoutInLayoutRequest) {

            checkThread();

            mLayoutRequested = true;

            scheduleTraversals(); // 安排事务!咱们继续进入

        }

    }

    @UnsupportedAppUsage

    void scheduleTraversals() {

        if (!mTraversalScheduled) {

            mTraversalScheduled = true;

            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

            mChoreographer.postCallback(

                    //继续跟进mTraversalRunnable回调

                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

            notifyRendererOfFramePending();

            pokeDrawLockIfNeeded();

        }

final class TraversalRunnable implements Runnable {

        @Override

        public void run() {

            doTraversal(); // 继续进入

        }

    }

 void doTraversal() {

        if (mTraversalScheduled) {

            mTraversalScheduled = false;

            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {

                Debug.startMethodTracing("ViewAncestor");

            }

            performTraversals(); // 来了,此处就是真正的measure、layout、draw入口了,里面会回调onMeasure、onLayout、onDraw,在此处先不展开分析

            if (mProfile) {

                Debug.stopMethodTracing();

                mProfile = false;

            }

           

        }

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值