Activity顯示到Window的過(guò)程

寫在前面的話

今天看到一個(gè)新聞垦搬,關(guān)于蝦米音樂(lè)的程序員寫的注釋。



好像圖都綠了艳汽,突然間想起在我曾經(jīng)老大的影響下猴贰,我的Log輸出已經(jīng)變成了fxxk。骚灸。這樣不好糟趾,不好。要改的,改改改义郑。



1. 從WindowManager的addView說(shuō)起

上次我們大致分析了Activity從創(chuàng)建到顯示的過(guò)程蝶柿,當(dāng)時(shí)只是簡(jiǎn)單分析了下。最終的顯示過(guò)程還是通過(guò)WindowManager的addView添加到Window上的非驮。WindowManager是個(gè)接口交汤,繼承了ViewManager,其最終的實(shí)現(xiàn)類是WindowManagerImpl劫笙,先來(lái)看下WindowManagerImpl的addView方法芙扎。

ActivityThread.java
if (a.mVisibleFromClient && !a.mWindowAdded) {
    a.mWindowAdded = true;
    wm.addView(decor, l);
}
WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    //調(diào)用了WindowManagerGlobal的addView方法
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
    // Only use the default token if we don't have a parent window.
    if (mDefaultToken != null && mParentWindow == null) {
        //判斷params
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        // Only use the default token if we don't already have a token.
        //給params設(shè)置token
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (wparams.token == null) {
            wparams.token = mDefaultToken;
        }
    }
}

上面的代碼是先給當(dāng)前的params設(shè)置了token,接著調(diào)用了WindowManagerGlobal的addView方法填大。


2. WindowManagerGlobal的addView方法

上面說(shuō)到我們對(duì)當(dāng)前的params設(shè)置了token戒洼,接下來(lái)調(diào)用了WindowManagerGlobal的addView方法。
在WindowManagerGlobal的addView方法中允华,首先進(jìn)行的是對(duì)參數(shù)的校驗(yàn)圈浇,包括了類型以及null的校驗(yàn)。如果不通過(guò)靴寂,則throw Exception磷蜀。接著在緩存的view中去尋找當(dāng)前的decorView是否已經(jīng)存在,如果已經(jīng)存在則需要判斷其是否正在“死亡”百炬,如果不是褐隆,這拋出異常。接著需要根據(jù)LayoutParams的type來(lái)判斷窗口的類型剖踊,默認(rèn)的是TYPE_APPLICATION即普通應(yīng)用窗口類型庶弃。如果是子窗口類型,則需要根據(jù)其token來(lái)查找其父窗口蜜宪。接著需要?jiǎng)?chuàng)建ViewRootImpl對(duì)象虫埂,并將decorView、root圃验、wparams以及添加到緩存中。下面是代碼:

WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    //參數(shù)檢查
    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) {
        //添加系統(tǒng)屬性改變的callback
        // 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);
        }
        //從緩存的view中尋找是否當(dāng)前的decorView已經(jīng)存在
        int index = findViewLocked(view, false);
        //index >= 0 則說(shuō)明已經(jīng)存在了缝呕,所以需要去讓它銷毀
        if (index >= 0) {
            //正在銷毀的view中是否包含澳窑,如果有的話,調(diào)用其doDie()方法
            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.
        // 判斷當(dāng)前的LayoutParams的type的類型供常,如果它是子窗口摊聋,那么需要去找到它的父窗口
        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);
                }
            }
        }
        //創(chuàng)建ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        //設(shè)置LayoutParams
        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 {
        //通過(guò)ViewRootImpl去設(shè)置view,傳入decorView栈暇,LayoutParams以及父窗口
        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;
    }
}

3. ViewRootImpl的setView方法

ViewRootImpl的setView方法比較長(zhǎng)麻裁,前一部分不用多說(shuō),其中有一部分比較重要的是對(duì)mSurfaceHolderCallback的賦值操作。這里面需要看到的是DecorView extends FrameLayout implements RootViewSurfaceTaker煎源,但是willYouTakeTheSurface這個(gè)方法返回的默認(rèn)是null色迂,如果需要設(shè)置可以在Activity中使用getWindow().takeSurface()設(shè)置。接著調(diào)用了requestLayout()方法手销,去請(qǐng)求開(kāi)始布局歇僧,這里面通過(guò)編舞者Choreographer來(lái)進(jìn)行的異步操作,整個(gè)過(guò)程也比較繁瑣锋拖。后面是對(duì)輸入通道的建立诈悍,通過(guò)Session將IWindow添加到WMS中。

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

            // Keep track of the actual window flags supplied by the client.
            mClientWindowLayoutFlags = attrs.flags;

            setAccessibilityFocus(null, null);
            //當(dāng)前的view是decorView兽埃,而通過(guò)源碼可以看到   DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks
            //所以這個(gè)條件成立侥钳,不過(guò)willYouTakeTheSurface這個(gè)方法去獲得Callback2的時(shí),
            //實(shí)際上獲取的是PhoneWindow的mTakeSurfaceCallback柄错,這個(gè)mTakeSurfaceCallback的設(shè)置是通過(guò)getWindow().takeSurface()這種方式設(shè)置慕趴。
            if (view instanceof RootViewSurfaceTaker) {
                //默認(rèn)的為null
                mSurfaceHolderCallback =
                        ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
                if (mSurfaceHolderCallback != null) {
                    mSurfaceHolder = new TakenSurfaceHolder();
                    mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
                }
            }

            // Compute surface insets required to draw at specified Z value.
            // TODO: Use real shadow insets for a constant max Z.
            if (!attrs.hasManualSurfaceInsets) {
                attrs.setSurfaceInsets(view, false /*manual*/, true /*preservePrevious*/);
            }

            CompatibilityInfo compatibilityInfo =
                    mDisplay.getDisplayAdjustments().getCompatibilityInfo();
            mTranslator = compatibilityInfo.getTranslator();
            //如果這個(gè)應(yīng)用沒(méi)有擁有自己的surface,那么需要開(kāi)啟硬件加速
            // If the application owns the surface, don't enable hardware acceleration
            if (mSurfaceHolder == null) {
                enableHardwareAcceleration(attrs);
            }

            ......
            //設(shè)置為已經(jīng)添加
            mAdded = true;
            int res; /* = WindowManagerImpl.ADD_OKAY; */

            // Schedule the first layout -before- adding to the window
            // manager, to make sure we do the relayout before receiving
            // any other events from the system.
            //開(kāi)始請(qǐng)求layout鄙陡,layout的過(guò)程是通過(guò)編舞者Choreographer來(lái)進(jìn)行的異步操作
            requestLayout();
            // 輸入通道的建立
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                mInputChannel = new InputChannel();
            }
            mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                    & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                //通過(guò)windowSession添加顯示冕房,這里面主要是創(chuàng)建SurfaceSession,SurfaceSession用來(lái)連接surface flinger
                //后面會(huì)通過(guò)native創(chuàng)建SurfaceControl時(shí)當(dāng)作參數(shù)傳入
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, 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();
                }
            }

            if (mTranslator != null) {
                mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
            }
            mPendingOverscanInsets.set(0, 0, 0, 0);
            mPendingContentInsets.set(mAttachInfo.mContentInsets);
            mPendingStableInsets.set(mAttachInfo.mStableInsets);
            mPendingVisibleInsets.set(0, 0, 0, 0);
            mAttachInfo.mAlwaysConsumeNavBar =
                    (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
            mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
            if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
            // 返回的結(jié)果不是成功趁矾,則根據(jù)結(jié)果拋出異常
            if (res < WindowManagerGlobal.ADD_OKAY) {
                mAttachInfo.mRootView = null;
                mAdded = false;
                mFallbackEventHandler.setView(null);
                unscheduleTraversals();
                setAccessibilityFocus(null, null);
                switch (res) {
                    ......
                }
                throw new RuntimeException(
                        "Unable to add window -- unknown error code " + res);
            }
            // 輸入事件的callback耙册,一般情況下都為null
            if (view instanceof RootViewSurfaceTaker) {
                mInputQueueCallback =
                    ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
            }
            // 注冊(cè)輸入事件的監(jiān)聽(tīng)
            if (mInputChannel != null) {
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());
            }
            //讓當(dāng)前的ViewRootImpl成為view的父親。毫捣。
            view.assignParent(this);
            mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
            mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;

            if (mAccessibilityManager.isEnabled()) {
                mAccessibilityInteractionConnectionManager.ensureConnection();
            }

            if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
            }

            // Set up the input pipeline.
            ......
        }
    }
}

4. Session的addToDisplay

Session的addToDisplay方法調(diào)用WMS的addWindow方法详拙,WMS中的addWindow主要是創(chuàng)建了一個(gè)WindowState對(duì)象,通過(guò)其attach方法調(diào)用Session中的windowAddedLocked方法創(chuàng)建SurfaceSession蔓同,通過(guò)SurfaceSession可以和surface flinger連接饶辙。

Session.java:
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

WMS:
public int addWindow(Session session, IWindow client, int seq,
        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        InputChannel outInputChannel) {
    int[] appOp = new int[1];
    //權(quán)限檢查實(shí)現(xiàn)類WindowManagerPolicy
    int res = mPolicy.checkAddPermission(attrs, appOp);
    if (res != WindowManagerGlobal.ADD_OKAY) {
        return res;
    }

    boolean reportNewConfig = false;
    WindowState parentWindow = null;
    long origId;
    final int callingUid = Binder.getCallingUid();
    final int type = attrs.type;

    synchronized(mWindowMap) {
        //錯(cuò)誤處理
        ......
        
        AppWindowToken atoken = null;
        final boolean hasParent = parentWindow != null;
        // Use existing parent window token for child windows since they go in the same token
        // as there parent window so we can apply the same policy on them.
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);
        // If this is a child window, we want to apply the same type checking rules as the
        // parent window type.
        final int rootType = hasParent ? parentWindow.mAttrs.type : type;

        boolean addToastWindowRequiresToken = false;

        if (token == null) {
        //錯(cuò)誤處理
            ......
            final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
            token = new WindowToken(this, binder, type, false, displayContent,
                    session.mCanAddInternalSystemWindow);
        } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
            atoken = token.asAppWindowToken();
            ......
        } ......//錯(cuò)誤處理
        } else if (token.asAppWindowToken() != null) {
            attrs.token = null;
            token = new WindowToken(this, client.asBinder(), type, false, displayContent,
                    session.mCanAddInternalSystemWindow);
        }
        
        //創(chuàng)建WindowState,
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);
        ......
        
        //開(kāi)啟輸入通道
        final boolean openInputChannels = (outInputChannel != null
                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            win.openInputChannel(outInputChannel);
        }

        //關(guān)于toast的一個(gè)處理
        ......

        res = WindowManagerGlobal.ADD_OKAY;
        if (mCurrentFocus == null) {
            mWinAddedSinceNullFocus.add(win);
        }

        if (excludeWindowTypeFromTapOutTask(type)) {
            displayContent.mTapExcludedWindows.add(win);
        }

        origId = Binder.clearCallingIdentity();
        //調(diào)用了WindowState的attach方法斑粱,下面有講解
        win.attach();
        //將WindowState和傳入的IWindow保存到mWindowMap中
        mWindowMap.put(client.asBinder(), win);
        ......

    return res;
}

WindowState.java:
void attach() {
    if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
    mSession.windowAddedLocked(mAttrs.packageName);
}

Session.java:
void windowAddedLocked(String packageName) {
    //設(shè)置包名
    mPackageName = packageName;
    mRelayoutTag = "relayoutWindow: " + mPackageName;
    if (mSurfaceSession == null) {
        //創(chuàng)建SurfaceSession與surface flinger連接
        mSurfaceSession = new SurfaceSession();
        
        mService.mSessions.add(this);
        if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
            mService.dispatchNewAnimatorScaleLocked(this);
        }
    }
    mNumWindow++;
}

SurfaceSession是通過(guò)native層創(chuàng)建的弃揽,能力不夠,不做分析则北。


6. Choreographer的TraversalRunnable異步方法執(zhí)行

ViewRootImpl中的Choreographer通過(guò)postCallback方式實(shí)現(xiàn)異步的遍歷操作矿微,Choreographer會(huì)在底層接收VSYNC(垂直同步)信號(hào),接收垂直同步信號(hào)后會(huì)根據(jù)類型來(lái)判斷具體操作尚揣。這里是遍歷操作涌矢,會(huì)調(diào)用TraversalRunnable的run方法,run方法中調(diào)用了ViewRootImpl的doTraversal方法快骗,doTraversal方法調(diào)用了performTraversals()方法娜庇。performTraversals()這里面就是真正的遍歷操作塔次。簡(jiǎn)化一下代碼(代碼太多了),這里面主要分為四部分:

  1. relayoutWindow名秀,這里會(huì)創(chuàng)建SurfaceControl励负,并且將之前的SurfaceSession當(dāng)作參數(shù)傳入。
  2. 調(diào)用performMeasure方法泰偿,對(duì)View進(jìn)行測(cè)量熄守。
  3. 調(diào)用performLayout對(duì)View的進(jìn)行布局。
  4. 調(diào)用performDraw進(jìn)行繪制耗跛。
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

void doTraversal() {
    ......
        performTraversals();
    ......
}


private void performTraversals() {
    // cache mView since it is used so much below...
    final View host = mView;
    ......
        try {
            ......
            //重新布局window裕照,主要是surface的創(chuàng)建
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            ......
        } 
        ......
        if (!mStopped || mReportNextDraw) {
                ......

                 // Ask host how big it wants to be
                 //測(cè)量
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                ......
        }
        ......
    if (didLayout) {
        //布局
        performLayout(lp, mWidth, mHeight);
        ......
    }
    ......
    if (!cancelDraw && !newSurface) {
        //繪制
        performDraw();
    } 
}

6.1 relayoutWindow的過(guò)程

這里的過(guò)程是比較多的囤耳,分步來(lái)說(shuō):

  1. 調(diào)用Session的relayout
ViewRootImpl.java:
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {
    ......
    //這里還是通過(guò)Session進(jìn)行relayout肿嘲,可以猜想Session中會(huì)調(diào)用WMS方法進(jìn)行真正relayout
    //這里說(shuō)下參數(shù)mSurface即Surface對(duì)象,這個(gè)對(duì)象是java層直接new出來(lái)的一個(gè)對(duì)象蚣驼,并沒(méi)有其他操作羔砾。
    //后面通過(guò)new SurfaceControl創(chuàng)建Surface并通過(guò)surfaceController.getSurface(outSurface)獲得真正的Surface
    int relayoutResult = mWindowSession.relayout(
            mWindow, mSeq, params,
            (int) (mView.getMeasuredWidth() * appScale + 0.5f),
            (int) (mView.getMeasuredHeight() * appScale + 0.5f),
            viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
            mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
            mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
            mSurface);

    ......
    return relayoutResult;
}
  1. Sessoin中的relayout又去調(diào)用WMS中的relayoutWindow方法(每次都是這個(gè)套路)
Session.java:
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int requestedWidth, int requestedHeight, int viewFlags,
        int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
        Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
        MergedConfiguration mergedConfiguration, Surface outSurface) {
    //果不其然负间,調(diào)用了WMS的relayoutWindow方法,
    int res = mService.relayoutWindow(this, window, seq, attrs,
            requestedWidth, requestedHeight, viewFlags, flags,
            outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
            outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);

    return res;
}

  1. WMS的relayoutWindow方法通過(guò)createSurfaceControl創(chuàng)建SurfaceControl姜凄,并通過(guò)WindowSurfaceController.getSurface將傳入的參數(shù)Surface真正賦值(copyFrom)
WMS:
public int relayoutWindow(Session session, IWindow client, int seq,
        WindowManager.LayoutParams attrs, int requestedWidth,
        int requestedHeight, int viewVisibility, int flags,
        Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
        Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
        MergedConfiguration mergedConfiguration, Surface outSurface) {
            ......
            result = win.relayoutVisibleWindow(mergedConfiguration, result, attrChanges,
                    oldVisibility);
            try {
                // 這里創(chuàng)建了SurfaceControl
                result = createSurfaceControl(outSurface, result, win, winAnimator);
            } catch (Exception e) {
                mInputMonitor.updateInputWindowsLw(true /*force*/);

                Slog.w(TAG_WM, "Exception thrown when creating surface for client "
                         + client + " (" + win.mAttrs.getTitle() + ")",
                         e);
                Binder.restoreCallingIdentity(origId);
                return 0;
            }
            ......
        
    Binder.restoreCallingIdentity(origId);
    return result;
}

WindowStateAnimator.java:
WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
    ......
    try {
        // 創(chuàng)建WindowSurfaceController政溃,可以看到這里面的參數(shù)有Session中的SurfaceSession對(duì)象
        // 之前也說(shuō)過(guò),SurfaceSession用來(lái)連接Surface flingler态秧,所以我覺(jué)得這里Surface和Surface flinger是通過(guò)SurfaceSession連接董虱。
        mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
                attrs.getTitle().toString(),
                width, height, format, flags, this, windowType, ownerUid);
        ......
    } catch (OutOfResourcesException e) {
        Slog.w(TAG, "OutOfResourcesException creating surface");
        mService.mRoot.reclaimSomeSurfaceMemory(this, "create", true);
        mDrawState = NO_SURFACE;
        return null;
    } catch (Exception e) {
        Slog.e(TAG, "Exception creating surface", e);
        mDrawState = NO_SURFACE;
        return null;
    }
    ......
    return mSurfaceController;
}

  1. createSurfaceControl方法通過(guò)調(diào)用WindowStateAnimator的createSurfaceLocked方法去創(chuàng)建WindowSurfaceController
WMS:
private int createSurfaceControl(Surface outSurface, int result, WindowState win,
        WindowStateAnimator winAnimator) {
    ......
    try {
        // 創(chuàng)建createSurfaceLocked
        surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
    } 
    ......
    if (surfaceController != null) {
        // 通過(guò)Surface.copyFrom方法獲得真正的Surface
        surfaceController.getSurface(outSurface);
        
    } else {
        ......
        outSurface.release();
    }
    return result;
}
  1. WindowSurfaceController的創(chuàng)建過(guò)程中通過(guò)判斷當(dāng)前是否處于debug狀態(tài)來(lái)判斷創(chuàng)建具體的SurfaceControl
WindowSurfaceController.java:
public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
        int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
    
    ......
        //創(chuàng)建SurfaceControl,從名字可以看出這是控制Surface的類申鱼,當(dāng)然了參數(shù)也包括了之前的SurfaceSession對(duì)象
        mSurfaceControl = new SurfaceControl(
                s, name, w, h, format, flags, windowType, ownerUid);
    ......
}
  1. SurfaceControl的具體創(chuàng)建過(guò)程是通過(guò)nativeCreate方法創(chuàng)建愤诱,當(dāng)然了參數(shù)包括了之前的SurfaceSession對(duì)象,之前也說(shuō)過(guò)捐友,SurfaceSession用來(lái)連接Surface flingler淫半,所以我覺(jué)得這里Surface和Surface flinger是通過(guò)SurfaceSession連接。
SurfaceControl.java:
public SurfaceControl(SurfaceSession session,
        String name, int w, int h, int format, int flags)
                throws OutOfResourcesException {
    if (session == null) {
        throw new IllegalArgumentException("session must not be null");
    }
    if (name == null) {
        throw new IllegalArgumentException("name must not be null");
    }

    mName = name;
    // 通過(guò)native方法創(chuàng)建匣砖,參數(shù)是名稱 SurfaceSession 寬 高等
    // 具體創(chuàng)建還是不清楚科吭。。
    mNativeObject = nativeCreate(session, name, w, h, format, flags);
    if (mNativeObject == 0) {
        throw new OutOfResourcesException(
                "Couldn't allocate SurfaceControl native object");
    }

    mCloseGuard.open("release");
}

執(zhí)行完relayoutWindow后脆粥,我們的mSurface對(duì)象不再是一個(gè)空洞洞的對(duì)象砌溺,而是一個(gè)真正的"“Surface”對(duì)象。


6.2 performMeasure方法

performMeasure方法執(zhí)行很簡(jiǎn)單变隔,就是調(diào)用了View的measure方法。

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        //調(diào)用了View的measure方法蟹倾,參數(shù)是長(zhǎng)和寬的測(cè)量規(guī)格
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

關(guān)于View的measure這里不多說(shuō)匣缘。


6.3 performLayout方法

performLayout主要是調(diào)用了View的layout方法猖闪,由于DecorView是FrameLayout的子類,F(xiàn)rameLayout又是ViewGroup的子類肌厨,而且ViewGroup的layout是final方法培慌,所以最后調(diào)用的是ViewGroup的layout方法,滿足條件的話柑爸,最后會(huì)調(diào)用View的layout方法吵护。

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    mLayoutRequested = false;
    mScrollMayChange = true;
    // 在布局過(guò)程中
    mInLayout = true;

    final View host = mView;
    if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
        Log.v(mTag, "Laying out " + host + " to (" +
                host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
    }

    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
        // 調(diào)用View(decorView)的layout方法
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        // 不在布局過(guò)程中了
        mInLayout = false;
        int numViewsRequestingLayout = mLayoutRequesters.size();
        if (numViewsRequestingLayout > 0) {
            // 這個(gè)注釋很清楚了,這里在布局的過(guò)程中主要是requestLayout()這個(gè)方法被調(diào)用了表鳍。
            // requestLayout() 這個(gè)方法會(huì)調(diào)用ViewRootImpl的requestLayoutDuringLayout方法馅而,將自己添加到list中
            // 這時(shí)候numViewsRequestingLayout > 0執(zhí)行下面的操作
            // 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;
}

6.4 performDraw的過(guò)程

前面說(shuō)了那么多,也沒(méi)輪到繪制的過(guò)程譬圣。當(dāng)測(cè)量完成瓮恭,布局完成,接下來(lái)便是真正的繪制的過(guò)程——performDraw厘熟。
整個(gè)繪制過(guò)程分為了中不同的繪制過(guò)程屯蹦,我看了很多博客,基本上分析的都是軟件繪制绳姨。但是登澜,Android 4.0就已經(jīng)默認(rèn)開(kāi)啟硬件加速了,所以這里重點(diǎn)說(shuō)下硬件繪制(不會(huì)涉及native層)飘庄。
硬件加速的渲染過(guò)程:

  1. 在ViewRootImpl的draw方法中脑蠕,如果開(kāi)啟了硬件加速會(huì)調(diào)用ThreadedRenderer的draw方法去繪制。
ViewRootImpl.java:
private void performDraw() {
    ......
    try {
        draw(fullRedrawNeeded);
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    ......
        try {
            mWindowSession.finishDrawing(mWindow);
        } catch (RemoteException e) {
        }
}

private void draw(boolean fullRedrawNeeded) {
    
    // 設(shè)置屬性和接口回調(diào)部分
    ......
    
    mAttachInfo.mDrawingTime =
            mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
    
    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
        // 開(kāi)啟了硬件加速竭宰,之前說(shuō)過(guò) 如果沒(méi)有自己的SurfaceView則開(kāi)啟硬件加速空郊,所以最終會(huì)執(zhí)行這部分代碼
        if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
            ......
            // ThreadedRenderer的draw方法
            mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
        } else {
            ......
            // 沒(méi)有開(kāi)啟硬件加速,軟件繪制
            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                return;
            }
        }
    }
    ......
}
  1. ThreadedRenderer調(diào)用updateRootDisplayList更新跟顯示列表切揭。
ThreadedRenderer.java:
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
    attachInfo.mIgnoreDirtyState = true;

    final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
    choreographer.mFrameInfo.markDrawStart();
    // 更新根顯示列表
    updateRootDisplayList(view, callbacks);

    ......
}
  1. updateRootDisplayList調(diào)用View.updateDisplayListIfDirty方法獲得RenderNode對(duì)象狞甚,通過(guò)DisplayListCanvas.drawRenderNode方法真正繪制。
ThreadedRenderer.java:
private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
    updateViewTreeDisplayList(view);

    if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
        DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
        try {
            final int saveCount = canvas.save();
            canvas.translate(mInsetLeft, mInsetTop);
            callbacks.onHardwarePreDraw(canvas);

            canvas.insertReorderBarrier();
            // view.updateDisplayListIfDirty()這個(gè)是重點(diǎn)廓旬,真正的將要顯示的列表交給canvas去繪制
            canvas.drawRenderNode(view.updateDisplayListIfDirty());
            canvas.insertInorderBarrier();

            callbacks.onHardwarePostDraw(canvas);
            canvas.restoreToCount(saveCount);
            mRootNodeNeedsUpdate = false;
        } finally {
            mRootNode.end(canvas);
        }
    }
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
  1. 關(guān)于View.updateDisplayListIfDirty()方法哼审,我的理解是canvas是整個(gè)畫布,通過(guò)draw孕豹,dispatchDraw將要畫的內(nèi)容添加到畫布上涩盾,通過(guò)renderNode.end(canvas)獲得當(dāng)前的畫布的內(nèi)容,最終交給GPU統(tǒng)一去處理励背。
View.java:
public RenderNode updateDisplayListIfDirty() {
    ......

        try {
            if (layerType == LAYER_TYPE_SOFTWARE) {// 軟件繪制
                buildDrawingCache(true);
                Bitmap cache = getDrawingCache(true);
                if (cache != null) {
                    canvas.drawBitmap(cache, 0, 0, mLayerPaint);
                }
            } else {
                computeScroll();

                canvas.translate(-mScrollX, -mScrollY);
                mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                // 如果沒(méi)有背景春霍,則直接調(diào)用dispatchDraw(canvas),否則調(diào)用draw(canvas)
                // Fast path for layouts with no backgrounds
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    dispatchDraw(canvas);
                    if (mOverlay != null && !mOverlay.isEmpty()) {
                        mOverlay.getOverlayView().draw(canvas);
                    }
                } else {
                    draw(canvas);
                }
            }
        } finally {
            // 繪制節(jié)點(diǎn)保存叶眉,我的理解是canvas是整個(gè)畫布址儒,通過(guò)draw芹枷,dispatchDraw將要畫的東西添加到畫布上,
            // 通過(guò)renderNode.end(canvas)保存畫布狀態(tài)莲趣,最終交給GPU去處理
            renderNode.end(canvas);
            // 設(shè)置alpha等屬性
            setDisplayListProperties(renderNode);
        }
    } else {
        mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
    }
    return renderNode;
}

軟件繪制過(guò)程:
drawSoftware直接調(diào)用了View的draw方法鸳慈,通過(guò)這個(gè)方法直接繪制。

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty) {


    try {
    ......
        try {
            ......
            // 真正繪制喧伞,在這里與硬件渲染對(duì)比可以看出這里面只是canvas去draw各種內(nèi)容走芋。
            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;
}

7. 寫在后面的話

這篇文章斷斷續(xù)續(xù)寫了將近兩周了,想大而全的全面分析所有方法潘鲫,發(fā)現(xiàn)東西太多了根本不能一下子全部分析完翁逞。這里面有很多地方都沒(méi)有寫出來(lái),包括了Choreographer(編舞者)次舌,View的measure熄攘、layout、draw的過(guò)程彼念,還有各種細(xì)節(jié)的實(shí)現(xiàn)以及底層native方法的具體實(shí)現(xiàn)挪圾。
路漫漫其修遠(yuǎn)兮,同志仍需努力逐沙。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哲思,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吩案,更是在濱河造成了極大的恐慌棚赔,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徘郭,死亡現(xiàn)場(chǎng)離奇詭異靠益,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)残揉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門胧后,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人抱环,你說(shuō)我怎么就攤上這事壳快。” “怎么了镇草?”我有些...
    開(kāi)封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵眶痰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我梯啤,道長(zhǎng)竖伯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任因宇,我火速辦了婚禮黔夭,結(jié)果婚禮上宏胯,老公的妹妹穿的比我還像新娘羽嫡。我一直安慰自己本姥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布杭棵。 她就那樣靜靜地躺著婚惫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪魂爪。 梳的紋絲不亂的頭發(fā)上先舷,一...
    開(kāi)封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音滓侍,去河邊找鬼蒋川。 笑死,一個(gè)胖子當(dāng)著我的面吹牛撩笆,可吹牛的內(nèi)容都是我干的捺球。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼夕冲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼氮兵!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起歹鱼,我...
    開(kāi)封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泣栈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后弥姻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體南片,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年庭敦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疼进。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡螺捐,死狀恐怖颠悬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情定血,我是刑警寧澤赔癌,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站澜沟,受9級(jí)特大地震影響灾票,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茫虽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一刊苍、第九天 我趴在偏房一處隱蔽的房頂上張望既们。 院中可真熱鬧,春花似錦正什、人聲如沸啥纸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)斯棒。三九已至,卻和暖如春主经,著一層夾襖步出監(jiān)牢的瞬間荣暮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工罩驻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穗酥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓惠遏,卻偏偏與公主長(zhǎng)得像砾跃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子爽哎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容