Android學(xué)習(xí)筆記---深入理解View#02

上篇文章中我們了解到了setContentView()背后所發(fā)生的事情.先用上一篇文章最后總結(jié)的圖片來回顧一下setContentView()的流程.


但是后來我發(fā)現(xiàn),我們只是知道了mDecor是怎樣與mContentParent聯(lián)系在一起并將我們的activity_main.xml布局添加到其中.而并不知道mDecor是如何添加到PhoneWindowActivity上的.所以本篇我們就來探究一下這個(gè)問題.

尋找我們的 mDecor

既然我們在PhoneWindow里沒有發(fā)現(xiàn)mDecor是如何添加到window上的.那么我就需要到別的地方去尋找mDecor的線索,那我們要該從哪入手呢?還記得上一篇我們從Activity的setContentView()出發(fā)時(shí),最終調(diào)用的是mWindow(PhoneWindow)setContentView()函數(shù),而mDecorPhoneWindow的成員變量,mWindowActivity的成員變量,那么我們可以在Activity的代碼中尋找一下我們的線索mDecor.
果然通過在Activity代碼中進(jìn)行mDecor的關(guān)鍵字搜索,我們找到了線索.

View mDecor = null;
void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
}

從上面的代碼可以看到Activity通過WindowManageraddView()方法將mDecor添加到Window上.但我們發(fā)現(xiàn)這里的mDecorPhoneWindow中的mDecor好像并不相同.而且在代碼中(Activity代碼中的mDecor)與(PhoneWindowmDecor)并沒有明顯的聯(lián)系在一起.難道線索就這么斷了?我決定使用絕招(搜索引擎),果然我找到了另一個(gè)切入點(diǎn).
經(jīng)過一番Internet后,我知道了View的布局繪制是發(fā)生在Activity的創(chuàng)建過程,而Activity的創(chuàng)建是由一個(gè)名為ActivityThread的類進(jìn)行控制的.下面我們來驗(yàn)證一下,看看能否找到線索.
首先我們找到ActivityThread的源碼,這里有兩種方法:

  1. 直接在Internet上閱讀在線的源碼
  2. 在Android Studio中新建一個(gè)簡單的工程,在MainActivityonCreate()函數(shù)中下一個(gè)斷點(diǎn),然后運(yùn)行調(diào)試.我們可以在調(diào)試工具的調(diào)用棧中找到ActivityThread.

找到了ActivityThread的源碼后,對makeVisible關(guān)鍵字進(jìn)行搜索,果然找到了調(diào)用的地方,在handleResumeActivity()中出現(xiàn)了makeVisible()的調(diào)用,既然出現(xiàn)了相關(guān)的調(diào)用,那我們就看一下handleResumeActivity()的代碼吧.

 final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        // 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
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;

            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;// 標(biāo)記1開始
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            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;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    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;
            } // 標(biāo)注 1結(jié)束

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r);

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            // 標(biāo)注 2開始
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                if (r.newConfig != null) {
                    r.tmpConfig.setTo(r.newConfig);
                    if (r.overrideConfig != null) {
                        r.tmpConfig.updateFrom(r.overrideConfig);
                    }
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                            + r.activityInfo.name + " with newConfig " + r.tmpConfig);
                    performConfigurationChanged(r.activity, r.tmpConfig);
                    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
                    r.newConfig = null;
                }
                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
                        + isForward);
                WindowManager.LayoutParams l = 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();
                }
            }
            // 標(biāo)注 2結(jié)束

            if (!r.onlyLocalRequest) {
                r.nextIdle = mNewActivities;
                mNewActivities = r;
                if (localLOGV) Slog.v(
                    TAG, "Scheduling idle handler for " + r);
                Looper.myQueue().addIdleHandler(new Idler());
            }
            r.onlyLocalRequest = false;

            // Tell the activity manager we have resumed.
            if (reallyResume) {
                try {
                    ActivityManagerNative.getDefault().activityResumed(token);
                } catch (RemoteException ex) {
                }
            }

        } 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) {
            }
        }
    }

代碼有點(diǎn)長,但我們只需關(guān)注兩個(gè)部分,我已經(jīng)在代碼上做了標(biāo)注.先來看一下標(biāo)注1的代碼.

            // 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;// 標(biāo)記1開始
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            // 關(guān)鍵部分
            if (r.window == null && !a.mFinished && willBeVisible) {
                // 1
                r.window = r.activity.getWindow(); 
                // 2
                View decor = r.window.getDecorView(); 
                decor.setVisibility(View.INVISIBLE);
                // 3
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                // 4
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    // 5
                    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;
            } // 標(biāo)注 1結(jié)束

其中這里的r,a分別是函數(shù)中的局部變量,r是一個(gè)ActivityClientRecord類型的對象,通過performResumeActivity()函數(shù)獲取,代表的是Activity執(zhí)行完onResume()之后的含有Activity相關(guān)狀態(tài)信息的對象.而a則是r所對應(yīng)的Activity.清楚了這兩個(gè)變量的意義后,我們可以對代碼進(jìn)行分析了.首先獲取了willBeVisible的值,它代表的是activity是否將要顯示在手機(jī)屏幕上.接著來到被標(biāo)注為關(guān)鍵部分if語句塊內(nèi)的代碼.代碼主要做了下面的事情(我已在上面的代碼中標(biāo)出了對應(yīng)的代碼句).

  1. r.window賦值為activity的mWindow(即PhoneWindow對象)
  2. 獲取r.window的DecorView對象(即我們PhoneWindowmDecor對象),并將其設(shè)置為不可見
  3. 獲取activity的mWindowManager對象和r.window的布局參數(shù)
  4. 將第2點(diǎn)獲取的DecorView對象賦值給a.mDecor((將PhoneWindowmDecor)與(ActivitymDecor)關(guān)聯(lián)起來)
  5. 通過第3點(diǎn)獲取的mWindowManager對象根據(jù)布局參數(shù)將mDecor添加到window上.

通過上面的代碼,我們成功的將mDecor添加到window當(dāng)中,但我們的mDecor還是不可見的,所以下面將要分析的標(biāo)注2的代碼就是讓mDecor變得可見.

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            // 標(biāo)注 2開始
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                if (r.newConfig != null) {
                    r.tmpConfig.setTo(r.newConfig);
                    if (r.overrideConfig != null) {
                        r.tmpConfig.updateFrom(r.overrideConfig);
                    }
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                            + r.activityInfo.name + " with newConfig " + r.tmpConfig);
                    performConfigurationChanged(r.activity, r.tmpConfig);
                    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
                    r.newConfig = null;
                }
                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
                        + isForward);
                WindowManager.LayoutParams l = 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();
                }
            }
            // 標(biāo)注 2結(jié)束

其實(shí)上面的代碼我們需要關(guān)心的就只有一句,就是r.activity.makeVisible();這句.這句話調(diào)用了我們之前在Activity源碼中找到的makeVisible()函數(shù),將mDecor設(shè)置為可見.到此,我們知道了mDecor是怎樣添加到window上的了.

是終點(diǎn)也是起點(diǎn)

作為一個(gè)熱愛技術(shù)的年輕人(碼農(nóng)),怎么可能會(huì)在這里就停止前進(jìn)的腳步呢?所以在上面makeVisible()函數(shù)中的一句代碼wm.addView(mDecor, getWindow().getAttributes());展開了新的起點(diǎn) - - - 繼續(xù)探索View的工作原理(希望能有新的發(fā)現(xiàn)).
我毫不猶豫的點(diǎn)進(jìn)了addView()的源碼,卻發(fā)現(xiàn)這是ViewManager接口的方法.

 /** Interface to let you add and remove child views to an Activity. To get an instance
  * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
  */
public interface ViewManager
{
    /**
     * Assign the passed LayoutParams to the passed View and add the view to the window.
     * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
     * errors, such as adding a second view to a window without removing the first view.
     * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
     * secondary {@link Display} and the specified display can't be found
     * (see {@link android.app.Presentation}).
     * @param view The view to be added to this window.
     * @param params The LayoutParams to assign to view.
     */
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

這個(gè)接口是用于將View添加到Activity或從Activity移除View的.既然這樣,那就去找它的實(shí)現(xiàn)類.從下面Activity的部分源碼中不難發(fā)現(xiàn)addView()是通過ActivitymWindowManger調(diào)用的,而ActivitymWindowManager是通過Window獲取的.

    private WindowManager mWindowManager;
    private Window mWindow;
    /** Retrieve the window manager for showing custom windows. */
    public WindowManager getWindowManager() {
        return mWindowManager;
    }
     final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
            ........
            mWindowManager = mWindow.getWindowManager();
            ........
     }

由此我們可以轉(zhuǎn)到Window的源碼中尋找mWindowManager的下落.在Window的源碼中,很容易就能找到我們的mWindowManager的相關(guān)代碼了.

    private WindowManager mWindowManager;
    /**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

這里的mWindowManager是通過WindowManagerImplcreateLocalWindowManager()函數(shù)進(jìn)行賦值的.所以繼續(xù)轉(zhuǎn)到WindowManagerImpl的代碼中.在WindowManagerImpl的代碼中終于發(fā)現(xiàn)了想要的addView()的代碼了.

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

可以看出WindowManagerImpl中的addView()調(diào)用的是mGlobal(WindowManagerGlobal)addView().看來我們的代碼還沒結(jié)束...自己挖的坑,再深也要走下去,繼續(xù)跳轉(zhuǎn)到WindowManagerGlobaladdView()代碼里.看來這次是到達(dá)了真正的目的地了.

    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, 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);
                    }
                }
            }
            // 標(biāo)注
            root = new ViewRootImpl(view.getContext(), display);

            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 {
            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;
        }
    }

由于代碼有點(diǎn)長,按照慣例,我們只關(guān)注有用的部分.在上面代碼中,我發(fā)現(xiàn)了一個(gè)setView()的函數(shù),相信這個(gè)函數(shù)就是我們想要的.那就看下這部分的代碼(從我在注釋中標(biāo)注的代碼開始).代碼中創(chuàng)建了一個(gè)名為rootViewRootImpl對象并調(diào)用了root.setView().既然調(diào)用了函數(shù),那按照我的風(fēng)格怎么都要進(jìn)去看看,萬一有意外收獲呢.
下面就是ViewRootImplsetView()的代碼(提示:下面代碼可以跳過):

    /**
     * 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;
                // Keep track of the actual window flags supplied by the client.
                mClientWindowLayoutFlags = attrs.flags;

                setAccessibilityFocus(null, null);

                if (view instanceof RootViewSurfaceTaker) {
                    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) {
                    final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
                    attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
                }

                CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
                mTranslator = compatibilityInfo.getTranslator();

                // If the application owns the surface, don't enable hardware acceleration
                if (mSurfaceHolder == null) {
                    enableHardwareAcceleration(attrs);
                }

                boolean restore = false;
                if (mTranslator != null) {
                    mSurface.setCompatibilityTranslator(mTranslator);
                    restore = true;
                    attrs.backup();
                    mTranslator.translateWindowLayout(attrs);
                }
                if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);

                if (!compatibilityInfo.supportsScreen()) {
                    attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                    mLastInCompatMode = true;
                }

                mSoftInputMode = attrs.softInputMode;
                mWindowAttributesChanged = true;
                mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
                mAttachInfo.mRootView = view;
                mAttachInfo.mScalingRequired = mTranslator != null;
                mAttachInfo.mApplicationScale =
                        mTranslator == null ? 1.0f : mTranslator.applicationScale;
                if (panelParentView != null) {
                    mAttachInfo.mPanelParentWindowToken
                            = panelParentView.getApplicationWindowToken();
                }
                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.
                
                // 標(biāo)注 , 我就是通過光明的入口
                requestLayout();
                
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    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);
                if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
                if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    switch (res) {
                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not valid; is your activity running?");
                        case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not for an application");
                        case WindowManagerGlobal.ADD_APP_EXITING:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- app for token " + attrs.token
                                    + " is exiting");
                        case WindowManagerGlobal.ADD_DUPLICATE_ADD:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- window " + mWindow
                                    + " has already been added");
                        case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
                            // Silently ignore -- we would have just removed it
                            // right away, anyway.
                            return;
                        case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window " + mWindow +
                                    " -- another window of this type already exists");
                        case WindowManagerGlobal.ADD_PERMISSION_DENIED:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window " + mWindow +
                                    " -- permission denied for this window type");
                        case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                            throw new WindowManager.InvalidDisplayException(
                                    "Unable to add window " + mWindow +
                                    " -- the specified display can not be found");
                        case WindowManagerGlobal.ADD_INVALID_TYPE:
                            throw new WindowManager.InvalidDisplayException(
                                    "Unable to add window " + mWindow
                                    + " -- the specified window type is not valid");
                    }
                    throw new RuntimeException(
                            "Unable to add window -- unknown error code " + res);
                }

                if (view instanceof RootViewSurfaceTaker) {
                    mInputQueueCallback =
                        ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                }
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

                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.
                CharSequence counterSuffix = attrs.getTitle();
                mSyntheticInputStage = new SyntheticInputStage();
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);

                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;
                mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
            }
        }
    }

Shit!(尼瑪),怎么感覺越陷越深.如果有這種感覺的同學(xué)那就對了,正所謂不如虎穴,焉得虎子.堅(jiān)持一下,光明就在前方.
在上面的一大串代碼中,我發(fā)現(xiàn)了一句非常熟悉的代碼requestLayout();(我已在上面的代碼進(jìn)行了標(biāo)注),這不是View在需要重新布局的時(shí)候調(diào)用的函數(shù)嗎,怎么會(huì)在這里出現(xiàn).正所謂Everything for a reason,我毫不猶豫的點(diǎn)了進(jìn)去.(各位同學(xué)肯定在想:還有完沒完...我只能說:相信我就跟我走下去,你會(huì)有所發(fā)現(xiàn)的).

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

這是停不下來的感覺啊,接著輪到scheduleTraversals(),我把其中相關(guān)的都貼出來了.

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

這里的scheduleTraversals()設(shè)置了回調(diào)mTraversalRunnable,而在回調(diào)里調(diào)用了doTraversal().

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

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

            performTraversals();

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

接著來到最終的一個(gè)函數(shù)了,就是上面在doTraversal()里調(diào)用的performTraversals().這個(gè)函數(shù)的代碼可不是一般的長,所以我也不再挑戰(zhàn)大家的極限了,我就直接給出performTraversals()函數(shù)里的3個(gè)關(guān)鍵的語句.

private void performTraversals() {
    .......
    // 測量過程
    performMeasure();
    .......
    // 布局過程
    performLayout();
    .......
    // 繪制過程
    performDraw();
    .......
}

是不是有點(diǎn)熟悉的感覺,這就是View的3大流程,measure,layout,draw,原來View的3大流程是在這里執(zhí)行的.到這里我們已經(jīng)簡略地走完了View的工作流程了.

總結(jié)

像上次一樣,最后還是用張圖總結(jié)本篇的內(nèi)容,這樣會(huì)使我們頭腦清晰一點(diǎn).

流程

上圖的箭頭表示了函數(shù)之間的調(diào)用,其中的CallBack:mTraversalRunnable一處代表的是設(shè)置回調(diào)函數(shù).
經(jīng)過本篇的學(xué)習(xí)我們發(fā)現(xiàn)了View的繪制是由performTraversals()函數(shù)發(fā)起的.下一篇開始我們就來研究研究繪制View的第一個(gè)流程Mesure.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末夺颤,一起剝皮案震驚了整個(gè)濱河市江场,隨后出現(xiàn)的幾起案子假丧,更是在濱河造成了極大的恐慌墓陈,老刑警劉巖吠勘,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件币厕,死亡現(xiàn)場離奇詭異曹宴,居然都是意外死亡城榛,警方通過查閱死者的電腦和手機(jī)揪利,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狠持,“玉大人疟位,你說我怎么就攤上這事〈梗” “怎么了甜刻?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長正勒。 經(jīng)常有香客問我得院,道長,這世上最難降的妖魔是什么昭齐? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮矾柜,結(jié)果婚禮上阱驾,老公的妹妹穿的比我還像新娘。我一直安慰自己怪蔑,他們只是感情好里覆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缆瓣,像睡著了一般喧枷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天隧甚,我揣著相機(jī)與錄音车荔,去河邊找鬼。 笑死戚扳,一個(gè)胖子當(dāng)著我的面吹牛忧便,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播帽借,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼珠增,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了砍艾?” 一聲冷哼從身側(cè)響起蒂教,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脆荷,沒想到半個(gè)月后凝垛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡简烘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年苔严,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孤澎。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡届氢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出覆旭,到底是詐尸還是另有隱情退子,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布型将,位于F島的核電站寂祥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏七兜。R本人自食惡果不足惜丸凭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腕铸。 院中可真熱鬧惜犀,春花似錦、人聲如沸狠裹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涛菠。三九已至莉御,卻和暖如春撇吞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背礁叔。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工牍颈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晴圾。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓颂砸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親死姚。 傳聞我的和親對象是個(gè)殘疾皇子人乓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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