全面剖析View的生命周期,基于Android 11

cab3eed2c8b81b2e6ba1c2f0a4bffeba.png

/   今日科技快讯   /

近日,浙江天猫技术有限公司、淘宝(中国)软件有限公司发生工商变更,董事长/总经理更新为淘宝天猫业务负责人戴珊;财务董事更新为李发光。此外,阿里巴巴网络科技有限公司财务董事已经由武卫更新为徐宏。

据悉,2021年12月6日,阿里巴巴集团董事长张勇发布全员信,任命戴珊代表集团分管中国数字商业板块;4月1日起,武卫不再担任阿里巴巴集团CFO,由徐宏接任。据了解,此次一系列变更是出于权责一致的法务安排。

/   作者简介   /

本篇文章来自android超级兵的投稿,文章主要分享了他对View生命周期的探索分析,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。

android超级兵的博客地址:

https://blog.csdn.net/weixin_44819566?type=blog

/   前言   /

最近在写materialDesign系列的博客,本来计划本篇为CoordinatorLayout源码分析,但是CoordinatorLayout涉及到了View的生命周期的知识,我又不想潦草的糊弄,所以那就穿插一篇View的生命周期吧。

2901df1bf6de1ca6795d34d46ea2af63.gif

源码基于: Android 11

/   整体流程   /

先来看看整体流程,然后再深入源码!

第一次加载

szj_TestActivity: activity onCreate start # activity onCreate 开始
szj_TestLifeView: onFinishInflate
szj_TestActivity: activity onCreate end # activity onCreate 结束
szj_TestActivity: activity onStart
szj_TestActivity: activity onResume
szj_TestLifeView: onAttachedToWindow
szj_TestLifeView: onWindowVisibilityChanged visibility:VISIBLE
szj_TestLifeView: onVisibilityChanged:changedView:TestLifeView visibility:VISIBLE
szj_TestLifeView: onMeasure
szj_TestLifeView: onMeasure
szj_TestLifeView: onSizeChanged w:300 h:300 oldW0: oldH0
szj_TestLifeView: onLayout changed:true left:0 top:0 right300: bottom300
szj_TestLifeView: onDraw
szj_TestLifeView: onWindowFocusChanged hasWindowFocus:true

切换到后台

szj_TestActivity: activity onPause
szj_TestLifeView: onWindowVisibilityChanged visibility:GONE
szj_TestLifeView: onWindowFocusChanged hasWindowFocus:false
szj_TestActivity: activity onStop
szj_TestLifeView: onVisibilityChanged:changedView:DecorView visibility:INVISIBLE

切换到前台

szj_TestLifeView: onWindowVisibilityChanged visibility:INVISIBLE
szj_TestActivity: activity onRestart
szj_TestActivity: activity onStart
szj_TestActivity: activity onResume
szj_TestLifeView: onVisibilityChanged:changedView:DecorView visibility:VISIBLE
szj_TestLifeView: onWindowVisibilityChanged visibility:VISIBLE
szj_TestLifeView: onDraw
szj_TestLifeView: onWindowFocusChanged hasWindowFocus:true

销毁 view

szj_TestActivity: activity onPause
szj_TestLifeView: onWindowFocusChanged hasWindowFocus:false
szj_TestLifeView: onWindowVisibilityChanged visibility:GONE
szj_TestActivity: activity onStop
szj_TestLifeView: onVisibilityChanged:changedView:DecorView visibility:INVISIBLE
szj_TestActivity: activity onDestroy
szj_TestLifeView: onDetachedFromWindow

流程图

dc984fa8a4e0e9561e5e0465a90d3076.png

tips

onCreate#setContentView()中的start 与 end 指的是这样。

19b2e6cd59c6575bd0645656526c7511.png

/   源码分析开始   /

framework的源码,我愿称之为终极甲骨文,我也没怎么看过,只知道个大概,所以这里就从android段的源码开始 (后续会补上)。

在activity启动过程中,会通过AMS调用到 ActivityThread.handleLaunchActivity() 和ActivityThread.handleResumeActivity() ,那么就从这两个方法开始!

ActivityThread#handleLaunchActivity()

# ActivityThread.java

public Activity handleLaunchActivity(ActivityClientRecord r,
                                     PendingTransactionActions pendingActions, Intent customIntent) {

  // szj 初始化 windowManagerGlobal 
  WindowManagerGlobal.initialize();

  // szj 
  final Activity a = performLaunchActivity(r, customIntent);
  return a;
}

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // szj 创建 activity 的上下文
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
     try {
       java.lang.ClassLoader cl = appContext.getClassLoader();
       // 通过反射创建 activity 的实例
       activity = mInstrumentation.newActivity(
         cl, component.getClassName(), r.intent);

        // 调用 activity#attach 创建 PhoneWindow 等
       activity.attach(appContext, this, getInstrumentation(), r.token,
                       r.ident, app, r.intent, r.activityInfo, title, r.parent,
                       r.embeddedID, r.lastNonConfigurationInstances, config,
                       r.referrer, r.voiceInteractor, window, r.configCallback,
                       r.assistToken);

        // szj 分发 onCreate() 事件
         mInstrumentation.callActivityOnCreate(activity, r.state);
        }catch(e){...}
 }

Tips

mInstrumentation:负责调用Activity和Application生命周期。在 ActivityThread#main()方法中创建。

26ea55776c8a86bb8db885d907db7e25.png

Activity#attach() 创建 PhoneWindow()。

39236637a2e993f4c2f1bb255cf7301e.png

所以这里需要关心的就是 :

  • 初始化:WindowManagerGlobal.initialize()

  • 初始化:PhoneWindow()

分发#Activity#onCreate()事件。

# Instrumentation.java

 public void callActivityOnCreate(Activity activity, Bundle icicle) {
           // 分发onCreate事件
       activity.performCreate(icicle);
    }
# Activity.java

final void performCreate(Bundle icicle) {
  performCreate(icicle, null);
}

 final void performCreate(Bundle icicle, PersistableBundle persistentState) {

   if (persistentState != null) {
     onCreate(icicle, persistentState);
   } else {
     // 出发onCreate() 事件
     onCreate(icicle);
   }
 }

tips:所有Activity的生命周期都是通过Instrumentation调用callActivityOnXXX() 来调用的,比如这里的callActivityOnCreate(),以及 下面介绍的callActivityOnResume()

ActivityThread#handleResumeActivity()

# ActivityThread.java

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
                                 String reason) {
   // ...
   // 分发 onResume 事件 
   final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
   final Activity a = r.activity;
   if (r.window == null && !a.mFinished && willBeVisible) {

       // 绑定window
       r.window = r.activity.getWindow();

       // 调用 PhoneWindow.getDecorView() 返回 DecorView
       View decor = r.window.getDecorView();

      // szj ViewManager
      ViewManager wm = a.getWindowManager();

       // 返回 w = LayoutParams.MATCH_PARENT, h = LayoutParams.MATCH_PARENT
      WindowManager.LayoutParams l = r.window.getAttributes();

     if (!a.mWindowAdded) {
       a.mWindowAdded = true;
       // 将 DecorView 添加到 window 上 (设置activity根视图) 关键!
       wm.addView(decor, l);
     }
   }
}

分发 onResume 事件:

c254073754dbf69b7c9d588403011c33.png

wm.addView( decor , l )方法上参数为:

  • @param decor : DecorView(FrameLayout)

  • @param l : LayoutOarams(width: MATCH_PARENT, height: MATCH_PARENT)

e80f92796eff16a1cb19b3ea1553e665.png

这里的 wm 是一个接口,实现类为 WindowManager,WindowManager 也是一个接口,最终实现类为 WindowManagerImpl。

最终执行到WindowManagerImpl$addView()方法上。

# WindowManagerImpl.java

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }
# WindowManagerGlobal.java

// @param view: DecorView
// @param params: w:match_parent h:match_parent
// @param parentWindow: PhoneWindow
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        ViewRootImpl root;

             //szj 实例化一个 ViewRootImpl
         root = new ViewRootImpl(view.getContext(), display);

        try 
          //szj 将 ViewRootImpl 与 DecorView 关联到一起
          root.setView(view, wparams, panelParentView, userId);
        } catch (RuntimeException e) { ... }
 }

调用到ViewRootImpl#setView()。

# ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
       if (mView == null) {
           // 将 DecorView 绑定到 ViewRootImpl.mView 属性上
           mView = view;

          ....
          requestLayout();
          ...
       }
}


@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
      // 检查是否在 UI线程
      checkThread();
      // 执行到这里
      scheduleTraversals();
    }
}


void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // szj handler 同步屏障
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // 将 UI 绘制任务发送到 Choreographer,回调触发 mTraversalRunnable,执行绘制操作
        mChoreographer.postCallback(
          Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
       ...
    }
}
# ViewRootImpl.java

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
      // szj 执行任务
      doTraversal();
    }
}

 // szj 同步屏障执行到这里
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        // 移除同步屏障
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        // szj view 开始测量 / 绘制 / 布局 真正执行生命周期
        performTraversals();

        ..
    }
}
private void performTraversals() {
       final View host = mView;

    /*
     * TODO 调用:
     *      1.View#onAttachedToWindow()  当 view 绑定 window 的时候
     *      2.View#onWindowVisibilityChanged() 当w indow 可见的时候调用
     *      3.View#onVisibilityChanged() 当 判断view是否隐藏时候调用,如果view.visibility = GONE 那么不执行测量绘制流程!
     */
    if (mFirst) {
      host.dispatchAttachedToWindow(mAttachInfo, 0);
    }

    if (viewVisibilityChanged) {
        // 当 window 可见的时候调用 会调用View#onWindowVisibilityChanged()
                host.dispatchWindowVisibilityChanged(viewVisibility);
    }

    if (layoutRequested) {

       // szj 执行这里 measureHierarchy 在 measureHierarchy() 中会多次调用 performMeasure() 来分发 onMeasure() 事件
       windowSizeMayChange |= measureHierarchy(host, lp, res,
                    desiredWindowWidth, desiredWindowHeight);
    }

    if (!mStopped || mReportNextDraw) {
      if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                        || mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||
                        updatedConfiguration) {
          // 分发 onMeasure()事件
          performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
      }

      if (measureAgain) {
        //   分发 onMeasure()事件
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
      }
    }

    if (didLayout) {
        // szjperformLayout 开始布局 分发 onLayout() 事件
        performLayout(lp, mWidth, mHeight);
    }

    if (!cancelDraw) {
        if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
            for (int i = 0; i < mPendingTransitions.size(); ++i) {
              mPendingTransitions.get(i).startChangingAnimations();
            }
            mPendingTransitions.clear();
        }

        // szjperformDraw 开始绘制 分发 onDraw() 事件 
        performDraw();
    }else {
      if (isViewVisible) {
        // 递归再次尝试 
        scheduleTraversals();
      } ...
    }
}

重点

  • View#dispatchAttachedToWindow(mAttachInfo, 0);

  • View#dispatchWindowVisibilityChanged(viewVisibility);

  • ViewRootImpl#measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight);

  • ViewRootImpl#performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

  • ViewRootImpl#performLayout(lp, mWidth, mHeight);

  • ViewRootImpl#performDraw();

View#dispatchAttachedToWindow(mAttachInfo, 0);

# View.java

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
      // View 生命周期1: 当 view 绑定 window 的时候调用
      onAttachedToWindow();

    int vis = info.mWindowVisibility;
    if (vis != GONE) {
      // View 生命周期2: 当 window 可见的时候调用
      onWindowVisibilityChanged(vis);
        ...
    }
    // View 生命周期3: 当判断view是否可见的时候调用 android:visibility="XXX"
       onVisibilityChanged(this, visibility);
}

这里需要注意的是会执行三个生命周期方法:

  • onAttachedToWindow(); 当 view 绑定 window 的时候调用

  • onWindowVisibilityChanged(int); 当 window 可见的时候调用

  • onVisibilityChanged(View, int); 当判断view是否可见的时候调用 android:visibility=“XXX”

View#dispatchWindowVisibilityChanged(viewVisibility);

# View.java

public void dispatchWindowVisibilityChanged(@Visibility int visibility) {
        onWindowVisibilityChanged(visibility);
    }

这个方法比较简单,直接就是当window试图发生变化的时候调用。

ViewRootImpl#measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight); 与

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

这里measureHierarchy()最终会调用到performMeasure()上,所以这两个就连起来聊了。

private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
                                 final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
    if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
        if (baseSize != 0 && desiredWindowWidth > baseSize) {
            childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                       // 分发 onMeasure() 事件 
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
          if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                    goodMeasure = true;
          } else {
                // 分发 onMeasure() 事件 
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
          }
        }
    }
}
# View.java

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            // 分发view.measure事件
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  if (forceLayout || needsLayout) {
    if (cacheIndex < 0 || sIgnoreMeasureCache) {
      // 开始测量
       onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  }
}

// 测量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  // 默认测量 
  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                       getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

// 默认测量 
public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        // 测量模式
        int specMode = MeasureSpec.getMode(measureSpec);
        // 测量大小
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
            // 未指定 即未限制 View 的大小 (常在 ScrollView 中使用,上一篇 NestedScrollView 就用到了!)
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;

            // 最大值
        case MeasureSpec.AT_MOST:
            // 具体值
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

这里通过performMeasure执行到了View#measure --> onMeasure()这里需要注意的是onMeasure会多次测量,至少2次。

测量模式就不过多介绍了,既然能看到这里,测量模式必然是都懂的!

ViewRootImpl#performLayout(lp, mWidth, mHeight);

# ViewRootImpl.java

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
       final View host = mView;
     host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());     
}
# View.java

public void layout(int l, int t, int r, int b) {
  // szj setOpticalFrame() / setFrame() 重点 调用 onSizeChanged() 方法
      boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
  if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            // szj 重点() 用来确定View的布局位置
            onLayout(changed, l, t, r, b);
    ...
  }
}

// 无论执行if 还是 else 都会执行setFrame()方法
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
        ....
        return setFrame(
                left   + parentInsets.left - childInsets.left,
                top    + parentInsets.top  - childInsets.top,
                right  + parentInsets.left + childInsets.right,
                bottom + parentInsets.top  + childInsets.bottom);
    }

无论isLayoutModeOptical(mParent)是true还是false,都会执行到setFrame()方法。

# View.java
protected int mRight;
protected int mLeft;
protected int mBottom;
protected int mTop;

protected boolean setFrame(int left, int top, int right, int bottom) {
    ... 
    if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
        int oldWidth = mRight - mLeft;
        int oldHeight = mBottom - mTop;
        int newWidth = right - left;
        int newHeight = bottom - top;
        boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
        if (sizeChanged) {
            // szj 调用 onSizeChanged() 方法
            sizeChange(newWidth, newHeight, oldWidth, oldHeight);
        }
      ...
    }
 }
// 在这里执行onSizeChangeed()方法,获取当前的新的宽高和老的宽高
private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
   // szj 在setFrame() 时候 调用onSizeChanged方法
   onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
}

这里执行了生命周期。

  • onSizeChanged(Int , Int , Int , Int) 返回了newWidth,newHeight,oldWidth,oldHeight

  • onLayout(boolean, Int,Int,Int,Int)

    • @param changed: 宽高是否发生变化

    • @param left top right bottom: 位置

一段代码看出他们的区别:

fun onLifeViewClick(v: View) {
    val temp = randomInt(20, -20)
    v.x += temp
    v.y += temp
    Log.d("Click", "temp:$temp")

  //        v.layoutParams.width = v.width + temp
  //        v.layoutParams.height = v.height + temp

    // 会重新测量绘制 onMeasure -> onSizeChanged() -> onLayout() -> onDraw()
    v.requestLayout()
}
private fun randomInt(m: Int, n: Int) = let {
      (Math.random() * (n - m + 1) + m).toInt()
}

这里比较简单,就不过多赘述了!

ViewRootImpl#performDraw();

#ViewRootImpl.java

 private void performDraw() {
  try {
     // 分发draw事件
     boolean canUseAsync = draw(fullRedrawNeeded);
  }finally {
    ...
  }
 }

private boolean draw(boolean fullRedrawNeeded) {
      boolean animating = mScroller != null && mScroller.computeScrollOffset();
    .... 
   // drawSoftware 分发 draw事件
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                      scalingRequired, dirty, surfaceInsets)) {
      return false;
    }

      if (animating) {
      // 重新分发任务
      scheduleTraversals();
    }
}

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

  // 分发draw事件
  mView.draw(canvas);
}
# View.java

public void draw(Canvas canvas) {
    // 绘制背景 
    drawBackground(canvas);
      if (!verticalEdges && !horizontalEdges) {
           // 用来View绘制
           onDraw(canvas);

        // 用来ViewGroup来绘制
        dispatchDraw(canvas);
    }
}

重点

  • onDraw() View来绘制

  • dispatchDraw() ViewGroup来绘制

小结

当AMS调用ActivityThread.handleResumeActivity的时候,会先分发Activity$onResume事件。分发完onResume事件后,就会获取 Activity#getWindowManager()与window.getDrcorView()。window 通过在AMS分发事件ActivityThread.handleLaunchActivity()方法中调用performLaunchActivity,创建activity,并且调用Activity#attach()创建的PhoneWindow。最终Activity#getWindowManager();的对象是ViewManager,实现类为 WindowManager ,WindowManager也是一个接口,最终实现类为 WindowManagerImpl,最终就会调用到 WindowManagerImpl.add() 上,有两个参数:

  • 参数一为:decorView

  • 参数二为:ViewGroup.LayoutParams(width:MATCH_PARENT, height: MATCH_PARENT )

WindowManagerImpl又会调用到WindowManagerGlobal#addView()上,在这个类中创建ViewRootImpl和DecorView的绑定,并且调用到ViewRootImpl.setView()方法上。

ViewRootImpl.setView()方法最终会调用到requestLayout()方法上,首先会通过checkThread()来检查线程是否相同,然后执行scheduleTraversals()方法,因为View的生命周期比其他事件重要很多,所以需要先开启同步屏障,优先执行这个任务,最终通过doTraversal()来调用performTraversals()执行View的生命周期。

在ViewRootImpl#performTraversals()中,通过host(View).dispatchAttachedToWindow()首先分发window和view绑定的一些回调事件。

  • View#onAttachedToWindow() 当view绑定 window 的时候

  • View#onWindowVisibilityChanged() 当window 可见的时候调用

  • View#onVisibilityChanged() 当 判断view是否隐藏时候调用,如果view.visibility = GONE 那么不执行测量绘制流程!

执行完这些后就紧接着开始执行:

  • performMeasure() 来多次分发View#measure() 事件

  • performLayout() 分发 View#layout() 事件

  • performDraw() 分发 View#draw() 事件

简易流程图:

0b4c513fd7fc1b5a857d07d58cb85864.png

在回过头来看看文章最开始的 View 生命周期流程:

第一次加载

szj_TestActivity: activity onCreate start # activity onCreate 开始
szj_TestLifeView: onFinishInflate
szj_TestActivity: activity onCreate end # activity onCreate 结束
szj_TestActivity: activity onStart
szj_TestActivity: activity onResume
szj_TestLifeView: onAttachedToWindow
szj_TestLifeView: onWindowVisibilityChanged visibility:VISIBLE
szj_TestLifeView: onVisibilityChanged:changedView:TestLifeView visibility:VISIBLE
szj_TestLifeView: onMeasure
szj_TestLifeView: onMeasure
szj_TestLifeView: onSizeChanged w:300 h:300 oldW0: oldH0
szj_TestLifeView: onLayout changed:true left:0 top:0 right300: bottom300
szj_TestLifeView: onDraw
szj_TestLifeView: onWindowFocusChanged hasWindowFocus:true

其他生命周期都介绍到了,那么onFinishInflate()在什么时候执行呢…

View#onFinishInflate()

众所周知,在onCreate的时候需要通过设置setContentView()来设置布局ID,那就就看看他的源码吧。

# MyActivity.java

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Log.e(TAG, "activity onCreate 开始")
    setContentView(R.layout.activity_xxx)
    Log.e(TAG, "activity onCreate 结束")
}
# appCompatActivity.java

@Override
public void setContentView(@LayoutRes int layoutResID) {
    initViewTreeOwners();
    getDelegate().setContentView(layoutResID);
}

getDelegate()是抽象类AppCompatDelegate,具体实现为 AppCompatDelegateImpl,所以最终会调用到 AppCompatDelegateImpl#setContentView()上。

# AppCompatDelegateImpl.java

@Override
public void setContentView(int resId) {
    ensureSubDecor();
    ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    mAppCompatWindowCallback.getWrapped().onContentChanged();
}

这里通过LayoutInflater.from().inflate()来解析,继续往下执行。

# LayoutInflater.java

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    // szj 走这里
    return inflate(resource, root, root != null);
}


 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                  + Integer.toHexString(resource) + ")");
        }


        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
        if (view != null) {
            // != null 直接返回
            return view;
        }
        XmlResourceParser parser = res.getLayout(resource);
        try {
            // szj setContentView 在这里解析
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

最终在inflate(XmlPullParser , ViewGroup , boolean )中解析xml。

# LayoutInflater.java

private static final String TAG_MERGE = "merge";

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
   final String name = parser.getName();
   if (TAG_MERGE.equals(name)) {
            // 如果最外层的标签是merge那么就走这里
      rInflate(parser, root, inflaterContext, attrs, false);
   }else {
      // szj 正常布局执行这里 来解析xml
      rInflateChildren(parser, temp, attrs, true);
   }
 }

这里提一嘴merge:

97e96c1c3d929497a1d6ada176d8b967.png

这东西我是不经常用,然后查了下官方文档,看着没啥用…就是能优化一点点布局,作用不大。

# LayoutInflater.java

final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    }

最终最终最终在这里解析。这里需要记住的就是参数4为true!

void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
           ....

        // 循环所有viewgroup来
        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

            final String name = parser.getName();

            if (...) {
              ..
            } else if (TAG_MERGE.equals(name)) {
               ....
            } else {
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
              // 通过递归的方式
                rInflateChildren(parser, view, attrs, true);
                // szj addView 最原始添加走这里 !
                viewGroup.addView(view, params);
            }
        }
        ... 

        // 解析完成之后 调用View#onFinishInflate() 方法
        if (finishInflate) {
            parent.onFinishInflate();
        }
    }

View#onFinishInflate() 方法终于找到了!当View 递归完成,吧每个view添加到viewgroup上的时候,就会调用View#onFinishInflate()。

忽然想到一道面试题。

在onCreate()中解析xml的数据,那么为啥view.getWidth() / view.getHeight() 没有值呢?通过本篇分析你应该十分清晰。因为在onResume的时候,才会通过ViewRootImpl来分发View的事件,只有View#onMeasure() 后,View才能有大小!

好了,本篇到此结束!

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

在微软工作365天,还你一个我眼中更加真实的微软

Android嵌套滑动,我用NestedScrollView

欢迎关注我的公众号

学习技术或投稿

ab3bbc41814aec2e44ecb067fba14a0c.png

1b13d9df2950c4eb4b5b434a1cfa4aa6.png

长按上图,识别图中二维码即可关注

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值