android中xml布局文件加载流程

简述

当我们在XML写好布局,然后通过Activity中onCreate方法中setContentView(R.layout.activity_path_measure);就可以把布局加载进入,然后把布局界面展现在我们眼前.十分简单,但是你是否了解它加载的过程........先让我们看一下图吧


image.png

加载过程

  • Activity中onCreate方法中,通过setContentView方法把布局文件加载进去
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //加载布局文件
    setContentView(R.layout.activity_path_measure);
}
  • setContentView方法是调用了setContentView(@LayoutRes int layoutResID)方法

     public void setContentView(@LayoutRes int layoutResID) {
      getWindow().setContentView(layoutResID);
      initWindowDecorActionBar();
      }
    

在这里我们主要看一下 getWindow().setContentView(layoutResID);这个方法

  • getWindow()是干嘛呢,请看看下面的代码

    public Window getWindow() {
      return mWindow;
    }
    

这mWindow又是什么玩意呢,我们慢慢找,我们可以在attach(,,,) 方法中找到mWindow生成方法;

     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,
        Window window) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);
    //这里就是mWindow的创建
    mWindow = new PhoneWindow(this, window);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    //在这里省略了好多代码..........
  }

从上面代码我们可以看出,mWindow其实是一个PhoeWindow,它是一个窗体,继承Window;

  • 好了,我们理解了 getWindow就是得到一个PhoeWindow,这里我们就可以回到以前那个方法

     getWindow().setContentView(layoutResID);
    

在这里我们得找到PhoeWindow这个类,然后找到setContentView这个方法,看到底是怎么一个加载过程,看一下PhoeWindow中setContentView的源码;

    @Override
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.

    //这个是第一步(1),mContentParent是什么玩意,其实他就是一个 ViewGroup容器,待把布局加载进去
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
  
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
    //这是第二步(2)
      //把布局加载进去,这个方法这里我就不深入了,以后有时间我单独讲一下LayoutInflater.inflate
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

从上面的源码我们分析得到,加载布局主要有二个步骤,刚开始mContentParent肯定是null,为null时走 installDecor();这个方法,这个后面会深入的讲解,不为null走mContentParent.removeAllViews()这个方法,这个方法很好理解,就是清空所有已加载的View,第二步就是把布局加载mContentParent中,mLayoutInflater.inflate(layoutResID, mContentParent);这个方法这里我不深入去讲解,有时间后面再单独拿出来讲解;

  • mContentParent这个是什么玩意呢...........先不说,看一下它为null时,走 installDecor()方法是否会赋值;

     private void installDecor() {
      if (mDecor == null) {
          mDecor = generateDecor();
          mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
          mDecor.setIsRootNamespace(true);
          if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
              mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
          }
      }
      if (mContentParent == null) {
          //我擦,就是这里赋值的,,,,,,哈哈好好看看这个方法,看这个方法前,要先了解mDecor 是什么玩意
          mContentParent = generateLayout(mDecor);
    
          // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
          mDecor.makeOptionalFitsSystemWindows();
          //这里也省略了N多代码
            }
    }
    

从上面源码,我们可以看到mContentParent是在这里赋值的,我们好好研究generateLayout(mDecor)这个方法,在研究这个方法前,我们把搞清楚mDecor又是什么玩意;

  • mDecor又是什么玩意呢,直接看源码

       protected DecorView generateDecor() {
     return new DecorView(getContext(), -1);
       }
    

从上面源码可以看出,mDecor就是 DecorView, DecorView是一个FrameLayout,看它的源码就很直观;

    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

    /* package */int mDefaultOpacity = PixelFormat.OPAQUE;

    /** The feature ID of the panel, or -1 if this is the application's DecorView */
    private final int mFeatureId;

    private final Rect mDrawingBounds = new Rect();

    private final Rect mBackgroundPadding = new Rect();

    private final Rect mFramePadding = new Rect();

    private final Rect mFrameOffsets = new Rect();

    //又省略N多代码.......................................

}
  • 好了,我们着重看一下 mContentParent 是如何赋值的,看一下 generateLayout(mDecor)的源码,我只是把重要的写出来,其他都省略了,为了不会看着头疼...................

    protected ViewGroup generateLayout(DecorView decor) {
      // Apply data from current theme.
      //这里得到主题样式,这个很好理解
      TypedArray a = getWindowStyle();
    
     //.............................省略了些................
    
    
      mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
      int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
              & (~getForcedWindowFlags());
      if (mIsFloating) {
          setLayout(WRAP_CONTENT, WRAP_CONTENT);
          setFlags(0, flagsToUpdate);
      } else {
          setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
      }
    
      if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
          requestFeature(FEATURE_NO_TITLE);
      } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
          // Don't allow an action bar if there is no title.
          requestFeature(FEATURE_ACTION_BAR);
      }
        //又要省略一大部分
    
    
    //以下代码就是根据样式加载系统的xml布局,,
      int layoutResource;
      int features = getLocalFeatures();
      // System.out.println("Features: 0x" + Integer.toHexString(features));
      if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
          layoutResource = R.layout.screen_swipe_dismiss;
      } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
          if (mIsFloating) {
              TypedValue res = new TypedValue();
              getContext().getTheme().resolveAttribute(
                      R.attr.dialogTitleIconsDecorLayout, res, true);
              layoutResource = res.resourceId;
          } else {
    
           // xml布局.可以从系统源码找到
              layoutResource = R.layout.screen_title_icons;
          }
          // XXX Remove this once action bar supports these features.
          removeFeature(FEATURE_ACTION_BAR);
          // System.out.println("Title Icons!");
      } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
              && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
          // Special case for a window with only a progress bar (and title).
          // XXX Need to have a no-title version of embedded windows.
          layoutResource = R.layout.screen_progress;
          // System.out.println("Progress!");
      } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
          // Special case for a window with a custom title.
          // If the window is floating, we need a dialog layout
          if (mIsFloating) {
              TypedValue res = new TypedValue();
              getContext().getTheme().resolveAttribute(
                      R.attr.dialogCustomTitleDecorLayout, res, true);
              layoutResource = res.resourceId;
          } else {
    
              layoutResource = R.layout.screen_custom_title;
          }
          // XXX Remove this once action bar supports these features.
          removeFeature(FEATURE_ACTION_BAR);
      } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
          // If no other features and not embedded, only need a title.
          // If the window is floating, we need a dialog layout
          if (mIsFloating) {
              TypedValue res = new TypedValue();
              getContext().getTheme().resolveAttribute(
                      R.attr.dialogTitleDecorLayout, res, true);
              layoutResource = res.resourceId;
          } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
              layoutResource = a.getResourceId(
                      R.styleable.Window_windowActionBarFullscreenDecorLayout,
                      R.layout.screen_action_bar);
          } else {
              layoutResource = R.layout.screen_title;
          }
          // System.out.println("Title!");
      } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
          layoutResource = R.layout.screen_simple_overlay_action_mode;
      } else {
          // Embedded, so no decoration is needed.
          //我们来看一下这个布局,其实其他都是一样,我只是拿这个来讲解一下
          layoutResource = R.layout.screen_simple;
          // System.out.println("Simple!");
      }
      //以下几行代码是重点
      View in = mLayoutInflater.inflate(layoutResource, null);
      decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
      mContentRoot = (ViewGroup) in;
    
    
    //contentParent就是这里赋值的,findViewById找到com.android.internal.R.id.content;这个ID的FrameLayout
    
    
      ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    
      return contentParent;
    

    }

从上面源码我们可以contentParent就是com.android.internal.R.id.content这个ID的FrameLayout; 而com.android.internal.R.id.content这个ID就是上面layoutResource布局中FrameLayout;在这里还是让大家看一下layoutResource布局文件吧,

  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
          android:inflatedId="@+id/action_mode_bar"
          android:layout="@layout/action_mode_bar"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:theme="?attr/actionBarTheme" />

//contentParent就是这个玩意啊...............................
<FrameLayout

     android:id="@android:id/content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:foregroundInsidePadding="false"
     android:foregroundGravity="fill_horizontal|top"
     android:foreground="?android:attr/windowContentOverlay" />

</LinearLayout>

好了,我们终于了解到contentParent是什么了,我们回到前面讲的installDecor()方法,这样就可以进行第二个步骤了 mLayoutInflater.inflate(layoutResID, mContentParent);就我们的布局加载到contentParent这个FrameLayout去了.差不多就这些了!总的看一下以下图;


image.png

本人做android开发多年,以后会陆续更新关于android高级UI,NDK开发,性能优化等文章,更多请关注我的微信公众号:谢谢!

image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345