天天看點

Activity:xml如何通過加載到UI界面

版本:27.1.1

觀看目錄:

  1. Activity:啟動流程
  2. Activity:xml如何通過加載到UI界面

結合

Activity:啟動流程

的流程圖:

如果對Activity的啟動流程不熟悉的請移步:Activity:啟動流程

下圖是從

Activity啟動

XML如何加載到UI界面

的:

圖有點大,可以點選圖檔放大檢視:

建議跟着源碼過一遍流程圖熟悉下,在看更容易了解!

Activity:xml如何通過加載到UI界面

本文章分析流程圖

截選自上圖後半部分。

Activity:xml如何通過加載到UI界面

本文分析分三個方面來說:

  1. setContentView為XML加載到UI做了哪些準備工作?
  2. setContentView之後Activity如何開始運轉工作的?
  3. 從開始運轉工作到測量繪制布局做了哪些工作?

setContentView

Activity:xml如何通過加載到UI界面

源碼:PhoneWindow中的setContentView

// 隻保留兩個核心方法
 @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
        // 初始化基礎布局
            installDecor();
        } 
      // ...省略n行代碼
            // Activity的初始布局中的FramLayout,已經完成xml布局的添加
            mLayoutInflater.inflate(layoutResID, mContentParent);
        
       // ...省略n行代碼
    }
           

installDecor()

初始化一個基礎布局:擷取DecorView、mContentParent

private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
           // ... 省略N行代碼
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            // ... 省略N行代碼
          
        }
    }

           
// 初始化的基礎布局也是一個XML檔案
    protected ViewGroup generateLayout(DecorView decor) {
       // ...省略N行代碼
       //通過findViewById在基礎布局中找到ViewGroup
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
       // ...省略N行代碼
        return contentParent;
    }
           

mLayoutInflater.inflate()

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
      	// ....省略N行代碼
      	// 擷取XmlResourceParser,用于解析XML
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
           
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
           // ...省略N行代碼
                    // Temp is the root view that was found in the xml
                    // 找到XMl根視圖
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                     // ...省略N行代碼
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                    //  addView
                        root.addView(temp, params);
                    }
            return result;
        }
    }

           

到這裡完成了第一步:

setContentView為XML加載到UI做了哪些準備工作?

setContentView之後Activity如何開始運轉工作的?

ActivityThread

中調用

performLaunchActivity()

會完成

Activity的啟動

以及

setContentView設定

,會得到Activity對象。

Activity a = performLaunchActivity(r, customIntent);
        if (a != null) {
        // ....省略
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}
           
// 無用代碼不再貼出
 final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
     //...省略N行
      ViewManager wm = a.getWindowManager();
      wm.addView(decor, l);

    }

           

從開始運轉工作到測量繪制布局做了哪些工作?

Activity:xml如何通過加載到UI界面

這裡都是代碼的跳轉不在一一貼源碼,直接看圖即可。

ActivityThread類:

handleResumeActivity()

=>

WindowManagerImpl類:

addView()

=>

WindowManagerGlobal類:

addView()

=>

ViewRootImpl類:

setView()

=>

這裡會觸發啟動,将參數和視圖交給ViewRootlmpl

ViewRootImpl類:

requestLayout()

=>

scheduleTraversals()

=>

doTraversal()

=>

performTraversals()

開始真正的

測量、布局、繪制

補充:

view.assignParent(this);

給View配置設定Parent,這裡傳的this,說明view的parent就是ViewRootIml

這裡就是吧自己配置設定給自己的子View.這也是mParent的由來

總結

  1. 在Activity建立後,調用setContentView設定XML,主要加載了一個基礎布局(基礎布局也是一個XML),

    完成頂層DecorView、和mContentParent容器的初始化

  2. 後調用inflate(),

    通過XmlResourceParser去解析設定的XML

  3. 通過

    createViewFromTag

    找到布局根視圖,将其添加到

    mContentParent

    容器中
  4. 然後handleResumeActivity中擷取

    ViewManager

    ,調用

    addView

    開始Activity的運轉
  5. 層層調用,會在WindowManagerGlobal類中調用ViewRootImpl中的

    setView觸發啟動

  6. setView中調用

    requestLayout()

    ,最終會調到

    performTraversals()

  7. 最後performTraversals()

    => performMeasure()=> performLayout()=>performDraw()

    完成最後的

    測量、布局、繪制

到這裡就已經完成

xml如何通過加載到UI界面

後續會繼續分析,是如何測量、布局、繪制。

Thanks!

繼續閱讀