1. 程式人生 > >ViewRootImpl的獨白,我不是一個View(佈局篇)

ViewRootImpl的獨白,我不是一個View(佈局篇)

前言

  • Activity的attach中生成PhoneWindow物件;
  • setContentView中初始化DecorView(ViewGroup);
  • 在LayoutInflater進行對佈局檔案的解析之後更加解析的資料
  • 根據解析出的資料執行View的建構函式進行View的構造,同時生成ViewTree。

為什麼接下來繼續寫這篇文章呢?是因為我在掘金上看到一篇子執行緒更新View的文章之後,發現自己對View還不是很了,以這個問題為方向看了View相關的原始碼。發現網路上有些文章對於ViewRootImpl的分析還是有些問題或者疑惑的,所以自己整理過的知識點分享給大家,希望能對大家有幫助。(原始碼cm12.1

View的介紹

最開始學習View的時候最先分析的是它的佈局(LinearLayout、FrameLayout、TableLayout、RelativeLayout、AbsoluteLayout),然後是它的三大方法(measure、layout、draw)。

繪製&載入View—–onMeasure()

  • MeasureSpec.EXACTLY是精確尺寸, 當我們將控制元件的layout_width或layout_height指定為具體數值時如andorid:layout_width=”50dip”,或者為FILL_PARENT是,都是控制元件大小已經確定的情況,都是精確尺寸。
  • MeasureSpec.AT_MOST是最大尺寸,當控制元件的layout_width或layout_height指定為WRAP_CONTENT時 ,控制元件大小一般隨著控制元件的子空間或內容進行變化,此時控制元件尺寸只要不超過父控制元件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控制元件允許的最大尺寸。
  • MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控制元件是AdapterView,通過measure方法傳入的模式。

ViewGroup.java

protected void onMeasure
(int widthMeasureSpec, int heightMeasureSpec) protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed)

繪製&載入View—–onLayout()

  • onLayout方法:是ViewGroup中子View的佈局方法。放置子View很簡單,只需在重寫onLayout方法,然後獲取子View的例項,呼叫子View的layout方法實現佈局。在實際開發中,一般要配合onMeasure測量方法一起使用。View的放置都是根據一個矩形空間放置的。
  • layout方法:是View的放置方法,在View類實現。呼叫該方法需要傳入放置View的矩形空間左上角left、top值和右下角right、bottom值。

繪製&載入View—–onDraw()

public void draw(Canvas canvas)
protected void onDraw(Canvas canvas)
protected void dispatchDraw(Canvas canvas)(View,ViewGroup)
protected boolean drawChild(Canvas canvas, View child, long drawingTime) (ViewGroup)
ViewTree.jpg

View的解析與生成

View的解析和生成之前在下邊的這兩篇文章中已經講述

在這兩篇文章中用到了一些Android中相關的類:

  • Activity:一個Activity是一個應用程式元件,提供一個螢幕,使用者可以用來互動為了完成某項任務,例如撥號、拍照、傳送email……。
  • View:作為所有圖形的基類。
  • ViewGroup:對View繼承擴充套件為檢視容器類。
  • Window:它概括了Android視窗的基本屬性和基本功能。(抽象類)
  • PhoneWindow:Window的子類。
  • DecorView:介面的根View,PhoneWindow的內部類。
  • ViewRootImpl:ViewRoot是GUI管理系統與GUI呈現系統之間的橋樑。
  • WindowManangerService:簡稱WMS,它的作用是管理所有應用程式中的視窗,並用於管理使用者與這些視窗發生的的各種互動。

ViewRootImpl簡介

The top of a view hierarchy, implementing the needed protocol between View and the WindowManager. This is for the most part an internal implementation detail of {@link WindowManagerGlobal}.

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    /*******部分程式碼省略**********/
}

ViewRootImpl是View中的最高層級,屬於所有View的根(但ViewRootImpl不是View,只是實現了ViewParent介面),實現了View和WindowManager之間的通訊協議,實現的具體細節在WindowManagerGlobal這個類當中。

ViewManager.png
public interface ViewManager{
    //新增View
    public void addView(View view, ViewGroup.LayoutParams params);
    //更新View
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    //移除View
    public void removeView(View view);
}

上面提到了WindowManagerView的新增、更新、刪除操作,那麼聯絡這兩者之間的Window呢?

Activity中的Window的setContentView 閱讀這篇文章我們知道Activity中有Window物件,一個Window物件對應著一個View(DecorView),ViewRootImpl就是對這個View進行操作的。

我們知道介面所有的元素都是有View構成的,介面上的每一個畫素點也都是由View繪製的。Window只是一個抽象的概念,把介面抽象為一個視窗物件,也可以抽象為一個View。

ViewRootImpl與其他類之間的關係

在瞭解ViewRootImpl與其他類的關係之前,我們先看一副圖和一段程式碼:

ViewRootImpl.jpg
public final class WindowManagerGlobal {
    /*******部分程式碼省略**********/
    //所有Window物件中的View
    private final ArrayList<View> mViews = new ArrayList<View>();
    //所有Window物件中的View所對應的ViewRootImpl
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    //所有Window物件中的View所對應的佈局引數
    private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
    /*******部分程式碼省略**********/
}
  • WindowManagerGlobal在其內部儲存著ViewRootImplView例項的對映關係(順序儲存)。

  • WindowManager繼承與ViewManger,從ViewManager這個類名來看就是用來對View類進行管理的,從ViewManager介面中的新增、更新、刪除View的方法也可以看出來WindowManagerView的管理。

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

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

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
    /*******部分程式碼省略**********/
}
  • WindowManagerImplWindowManager的實現類。WindowManagerImpl內部方法實現都是由代理類WindowManagerGlobal完成,而WindowManagerGlobal是一個單例,也就是一個程序中只有一個WindowManagerGlobal物件服務於所有頁面的View。

ViewRootImpl的初始化

在Activity的onResume之後,當前Activity的Window物件中的View會被新增在WindowManager中。

public final class ActivityThread {
    /*******部分程式碼省略**********/
    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        /*******部分程式碼省略**********/

        // TODO Push resumeArgs into the activity for consideration
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            /*******部分程式碼省略**********/
            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;
                //window的型別:一個應用視窗型別(所有的應用視窗型別都展現在最頂部)。
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //將decor新增在WindowManager中
                    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.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

            /*******部分程式碼省略**********/

        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null, false);
            } catch (RemoteException ex) {
            }
        }
    }
}

建立Window所對應的ViewRootImpl,並將Window所對應的View、ViewRootImpl、LayoutParams順序新增在WindowManager中。

public final class WindowManagerGlobal {
    /*******部分程式碼省略**********/
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //檢查引數是否合法
        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 and we're running on L or above (or in the
            // system context), assume we want hardware acceleration.
            final Context context = view.getContext();
            if (context != null
                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        //宣告ViwRootImpl
        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);
                    }
                }
            }
            //建立ViwRootImpl
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            //將Window所對應的View、ViewRootImpl、LayoutParams順序新增在WindowManager中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            //把將Window所對應的View設定給建立的ViewRootImpl
            //通過ViewRootImpl來更新介面並完成Window的新增過程。
            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;
        }
    }
}

ViewRootImpl繫結Window所對應的View

ViewRootImpl繫結Window所對應的View,並對該View進行測量、佈局、繪製等。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    /*******部分程式碼省略**********/
    /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                //ViewRootImpl成員變數view進行復制,以後操作的都是mView。
                mView = view;
                /*******部分程式碼省略**********/
                //Window在新增完之前先進行一次佈局,確保以後能再接受系統其它事件之後重新佈局。
                //對View完成非同步重新整理,執行View的繪製方法。
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //將該Window新增到螢幕。
                    //mWindowSession實現了IWindowSession介面,它是Session的客戶端Binder物件.
                    //addToDisplay是一次AIDL的跨程序通訊,通知WindowManagerService新增IWindow
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
                /*******部分程式碼省略**********/
                //設定當前View的mParent
                view.assignParent(this);
                /*******部分程式碼省略**********/
            }
        }
    }
}

ViewRootImpl對mView進行操作

對View的操作包括文章最開始講述的測量、佈局、繪製,其過程主要是在ViewRootImpl的performTraversals方法中。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    /*******部分程式碼省略**********/
    //請求對介面進行佈局
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    //校驗所線上程,mThread是在ViewRootImpl初始化的時候執行mThread = Thread.currentThread()進行賦值的,也就是初始化ViewRootImpl所在的執行緒。
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    //安排任務
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
        }
    }

    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    //做任務
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
            try {
                //執行任務
                performTraversals();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

    //執行任務(分別呼叫mView的measure、layout、draw)
    private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        /*******部分程式碼省略**********/
        //想要展示視窗的寬高
        int desiredWindowWidth;
        int desiredWindowHeight;

        //開始進行佈局準備
        if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null) {
            /*******部分程式碼省略**********/
            if (!mStopped) {
                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);


                     // 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 (measureAgain) {
                        //View的測量
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    }

                    layoutRequested = true;
                }
            }
        } else {
            /*******部分程式碼省略**********/
        }

        final boolean didLayout = layoutRequested /*&& !mStopped*/ ;
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            //View的佈局
            performLayout(lp, desiredWindowWidth, desiredWindowHeight);

            /*******部分程式碼省略**********/
        }
        /*******部分程式碼省略**********/

        if (!cancelDraw && !newSurface) {
            if (!skipDraw || mReportNextDraw) {
                /*******部分程式碼省略**********/
                //View的繪製
                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;
    }
}

View的測量

ViewRootImpl呼叫performMeasure執行Window對應的View的測量。

  • ViewRootImpl的performMeasure;
  • DecorView(FrameLayout)的measure;
  • DecorView(FrameLayout)的onMeasure;
  • DecorView(FrameLayout)所有子View的measure;
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    /*******部分程式碼省略**********/
    //View的測量
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            //mView在Activity中為DecorView(FrameLayout)
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
}

View的佈局

ViewRootImpl呼叫performLayout執行Window對應的View的佈局。

  • ViewRootImpl的performLayout;
  • DecorView(FrameLayout)的layout方法;
  • DecorView(FrameLayout)的onLayout方法;
  • DecorView(FrameLayout)的layoutChildren方法;
  • DecorView(FrameLayout)的所有子View的Layout;

在這期間可能View會自己觸發布布局請求,所以在此過程會在此呼叫ViewRootImpl的requestLayout重新進行測量、佈局、繪製。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    /*******部分程式碼省略**********/

    //請求對ViewRootImpl進行佈局
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

    //View的佈局
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        /*******部分程式碼省略**********/
        final View host = mView;
        /*******部分程式碼省略**********/
        try {
            //呼叫View的Layout方法進行佈局
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
            mInLayout = false;
            //在ViewRootImpl進行佈局的期間,Window內的View自己進行requestLayout
            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佈局,最終回撥到ViewRootImpl的requestLayout進行重新測量、佈局、繪製
                        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);
                                    /*******部分程式碼省略**********/
                                    //請求對該View佈局,最終回撥到ViewRootImpl的requestLayout進行重新測量、佈局、繪製
                                    view.requestLayout();
                                }
                            }
                        });
                    }
                }

            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        mInLayout = false;
    }
}

View的繪製

ViewRootImpl呼叫performDraw執行Window對應的View的佈局。

  • ViewRootImpl的performDraw;
  • ViewRootImpl的draw;
  • ViewRootImpl的drawSoftware;
  • DecorView(FrameLayout)的draw方法;
  • DecorView(FrameLayout)的dispatchDraw方法;
  • DecorView(FrameLayout)的drawChild方法;
  • DecorView(FrameLayout)的所有子View的draw方法;
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    /*******部分程式碼省略**********/
    //View的繪製
    private void performDraw() {
        if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
            return;
        }

        final boolean fullRedrawNeeded = mFullRedrawNeeded;
        mFullRedrawNeeded = false;

        mIsDrawing = true;
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
        try {
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        /*******部分程式碼省略**********/
    }
    //進行繪製
    private void draw(boolean fullRedrawNeeded) {

        /*******部分程式碼省略**********/
        //View上新增的Observer進行繪製事件的分發
        mAttachInfo.mTreeObserver.dispatchOnDraw();

        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                /*******部分程式碼省略**********/
                //呼叫Window對應的ViewRootImpl的invalidate方法
                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
            } else {
                /*******部分程式碼省略**********/
                //繪製Window
                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
            }
        }

        if (animating) {
            mFullRedrawNeeded = true;
            scheduleTraversals();
        }
    }

    void invalidate() {
        mDirty.set(0, 0, mWidth, mHeight);
        if (!mWillDrawSoon) {
            scheduleTraversals();
        }
    }

    /**
     * @return true if drawing was successful, false if an error occurred
     */
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        /*******部分程式碼省略**********/
        try {
            /*******部分程式碼省略**********/
            try {
                canvas.translate(-xoff, -yoff);
                if (mTranslator != null) {
                    mTranslator.translateCanvas(canvas);
                }
                canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                attachInfo.mSetIgnoreDirtyState = false;
                //View繪製
                mView.draw(canvas);

                drawAccessibilityFocusedDrawableIfNeeded(canvas);
            } finally {
                if (!attachInfo.mSetIgnoreDirtyState) {
                    // Only clear the flag if it was not set during the mView.draw() call
                    attachInfo.mIgnoreDirtyState = false;
                }
            }
        } finally {
            /*******部分程式碼省略**********/
        }
        return true;
    }
}

非同步執行緒更新檢視

非同步執行緒中到底是否能對檢視進行更新呢?我們先看看TextView.setText方法的程式碼執行流程圖。

setText.png

從上圖看在頁面進行檢視更新的時候會觸發checkThread,校驗當前執行緒是否是ViewRootImpl被建立時所在的執行緒。而ViewRootImpl的建立是在Activity的onResume生命週期之後。

  • 我們可以再onResume之前在非同步執行緒進行檢視更新,因為這個時候不會發生執行緒校驗。
  • 我們可以再非同步執行緒初始化ViewRootImpl同時在該執行緒進行檢視更新。

PS:我們學習Android原始碼並通過了解其內部實現,我們可以通過技術手段去掉、增加或修改部分程式碼邏輯。以期望能做出更好的產品、做更細節的優化。但是想這種介面繪製只能發生在主執行緒規則我們還是必須要遵守的。兩個執行緒不能同時draw,否則螢幕會花;不能同時寫一塊記憶體,否則記憶體會花;不能同時寫一份檔案,否則檔案會花。同一時刻只有一個執行緒可以做ui,那麼當兩個執行緒互斥機率較大時,或者保證互斥的程式碼複雜時,選擇其中一個長期持有其他發訊息就是典型的解決方案。所以普遍的要求ui只能單執行緒。

Activity、Dialog、Toast的ViewRootImpl的不同

文章前面內容都是站在Activity的角度來進行程式碼解析的,因此我們不再對Dialog和Toast與Activity做具體分析,主要來看看它們與Activity有什麼不同之處。詳見:Dialog、Toast的Window和ViewRootImpl

總結

通過對ViewRootImpl的更細節的分析,我們再看自定義View的佈局時的一些方法會更加清楚(知其然且知其所以然)。同時也解開了Android中非同步執行緒更新View的謎底。

文章到這裡就全部講述完啦,若有其他需要交流的可以留言哦~!~!

想閱讀作者的更多文章,可以檢視我 個人部落格 和公共號:

振興書城