Android中view的添加源碼分析

問題:為什么在Activity的onCreate()、onStart()缚甩、onResume()方法中直接通過 View.getHeight() 和 View.getMeasuredHeight() 方法都拿不到View的寬高呢饺蚊?
首先我們來看Activity的onResume是在什么時候被調(diào)用的讨便,在ActivityThread.java 中執(zhí)行handleResumeActivity()方法

/***
 *在ActivityThread.java 中
 */
@Override
 public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        
        //performResumeActivity執(zhí)行Activity的onResume()方法
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);  //注釋 Ⅰ
        if (r == null) {
            // We didn't actually resume the activity, so skipping any follow-up actions.
            return;
        }

        final Activity a = r.activity;

        final int forwardBit = isForward
                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

        // If the window hasn't yet been added to the window manager,
        // and this guy didn't finish itself or start another activity,
        // then go ahead and add the window.
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityManager.getService().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        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 (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //開始添加 view 磕蛇,在wm.addView()方法中會真正調(diào)用view的測量命锄、布局、繪制
                    wm.addView(decor, l); //注釋 Ⅳ
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
        //省略部分代碼...
    }
/** 注釋 Ⅰ 的實現(xiàn)
* 在ActivityThread.java中
*/
 public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
        //省略部分代碼..
     r.activity.performResume(r.startsNotResumed, reason); //注釋 Ⅱ
 }

/** 注釋 Ⅱ 的實現(xiàn)
*在 Activity.java 中
*/
final void performResume(boolean followedByPause, String reason) {
        //省略其他代碼... 
        // mResumed is set by the instrumentation
        mInstrumentation.callActivityOnResume(this); //注釋 Ⅲ
}

/** 注釋  Ⅲ  的實現(xiàn)
* Instrumentation.java中
*/
 public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
     //在這里就真正執(zhí)行了Activity的onResume()方法的調(diào)用
        activity.onResume(); 
     //省略部分代碼..
 }

因此在ActivityThread類中的handleResumeActivity()里面調(diào)用performResumeActivity()完成了調(diào)用Activity類的onResume()方法的調(diào)用万哪。緊接在handleResumeActivity()方法執(zhí)行完performResumeActivity()后侠驯,又執(zhí)行了wm.addView() (注釋 Ⅳ),完成View的添加(包括測量奕巍、布局吟策、繪制)。
在handleResumeActivity()方法中的wm.addView()添加view伍绳,即調(diào)用了WindowManager的實現(xiàn)了WindowManagerImpl中的addView()方法踊挠,然后在乍桂。WindowManagerImpl中的addView()方法里面又調(diào)用WindowManagerGlobal的addView()方法去添加view

/**
* WindowManagerImpl.java
*/
public final class WindowManagerImpl implements WindowManager {
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); //注釋 Ⅴ
    }
}

/** 注釋 Ⅴ 的實現(xiàn)
* WindowManagerGlobal.java
*/
public final class WindowManagerGlobal {
     public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //省略部分代碼....
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
           //省略部分代碼....

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            //添加view到List集合中
            mViews.add(view); 
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                //root是ViewRootImpl  在里面實現(xiàn)view的添加
                root.setView(view, wparams, panelParentView); //注釋 Ⅵ
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
}

在ViewRootImpl中的setView()方法里面會調(diào)用requestLayout()方法請求布局

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        
     /** 注釋 Ⅵ 的實現(xiàn)
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        //省略部分代碼....
        
        // 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.
        //請求布局
        requestLayout(); //注釋 Ⅶ
        
        //省略部分代碼....
    }
            
    /** 注釋 Ⅶ 的實現(xiàn)
    * 在requestLayout()中調(diào)用scheduleTraversals()
    */
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals(); //注釋Ⅷ
        }
    }
     /** 注釋 Ⅷ 的實現(xiàn)
     *在scheduleTraversals()方法中有個異步操作mTraversalRunnable
     */
     void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);   //注釋 Ⅸ
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    } 
    /** 注釋 Ⅸ 的實現(xiàn)
    *ViewRootImpl里的內(nèi)部類冲杀,執(zhí)行doTraversal()
    */
   final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal(); // 注釋 Ⅹ
        }
    }
       /** 注釋 Ⅹ 的實現(xiàn)
       *在doTraversal方法中執(zhí)行 performTraversals();
       */
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            //在此方法中真正調(diào)用了ViewGroup的布局、測量睹酌、繪制
            performTraversals();  

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    } 
     /**
     * 在performTraversals()方法中執(zhí)行View的測量权谁、布局、繪制
     */
     private void performTraversals() {
        //省略部分代碼..
          // Ask host how big it wants to be
         //測量憋沿、布局旺芽、繪制
          performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
          performLayout(lp, mWidth, mHeight);
          performDraw();
     }
}

在ViewRootImpl類中 performMeasure()的實現(xiàn)

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            //開始view的測量,這里的 mView是 DecorView
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

在ViewRootImpl類中 performLayout()的實現(xiàn)

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
        mLayoutRequested = false;
        mScrollMayChange = true;
        mInLayout = true;

        final View host = mView;
        if (host == null) {
            return;
        }
        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
            Log.v(mTag, "Laying out " + host + " to (" +
                    host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
        }
      Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
        try {
            //開始布局 這里的host是 DecorView
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        }
     //省略部分代碼....
}

在ViewRootImpl類中performDraw()的實現(xiàn)

private void performDraw() {
    //省略其他代碼....
    //開始繪制
    boolean canUseAsync = draw(fullRedrawNeeded);
}

之所以在onCreate()辐啄、onStart()采章、onResume()里通過View.getHeight 和 View.getMeasuredHeight()方法拿到的都是0,是因為在onResume()方法執(zhí)行完后才進行view的添加(包括測量壶辜、布局悯舟、繪制)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砸民,一起剝皮案震驚了整個濱河市抵怎,隨后出現(xiàn)的幾起案子奋救,更是在濱河造成了極大的恐慌,老刑警劉巖反惕,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尝艘,死亡現(xiàn)場離奇詭異,居然都是意外死亡姿染,警方通過查閱死者的電腦和手機背亥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悬赏,“玉大人隘梨,你說我怎么就攤上這事∠衔耍” “怎么了轴猎?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長进萄。 經(jīng)常有香客問我捻脖,道長,這世上最難降的妖魔是什么中鼠? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任可婶,我火速辦了婚禮,結(jié)果婚禮上援雇,老公的妹妹穿的比我還像新娘矛渴。我一直安慰自己,他們只是感情好惫搏,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布具温。 她就那樣靜靜地躺著,像睡著了一般筐赔。 火紅的嫁衣襯著肌膚如雪铣猩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天茴丰,我揣著相機與錄音达皿,去河邊找鬼。 笑死贿肩,一個胖子當著我的面吹牛峦椰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汰规,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼汤功,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了控轿?” 一聲冷哼從身側(cè)響起冤竹,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤拂封,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鹦蠕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冒签,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年钟病,在試婚紗的時候發(fā)現(xiàn)自己被綠了萧恕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡肠阱,死狀恐怖票唆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屹徘,我是刑警寧澤走趋,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站噪伊,受9級特大地震影響簿煌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鉴吹,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一姨伟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豆励,春花似錦夺荒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诚啃,卻和暖如春淮摔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背始赎。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工旷祸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钓试,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓姻乓,卻偏偏與公主長得像晰搀,于是被迫代替她去往敵國和親五辽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349