android View生命周期

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布[2022-4-26]

android 甲骨文之 View 生命周期

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

0_68139c624226b4df3b7d1febb1b39d72

源码基于: android-30

整体流程

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

  • 第一次加载:

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

流程图:

image-20220418195407058

tips:

onCreate#setContentView() startonCreate#setContentView() end 指的是这样

carbon

源码分析开始

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()方法中创建

    view_2

  • Activity#attach() 创建 PhoneWindow()

view_3

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

  • 初始化: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 事件:

view_5

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

  • @param decor : DecorView(FrameLayout)

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

view_4

这里的 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()
}
效果一效果二
git_1gif_2

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

VoewRootImpl#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,实现类为 WindowManagerWindowManager也是一个接口,最终实现类为 WindowManagerImpl,最终就会调用到 WindowManagerImpl.add() 上,有两个参数:

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

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

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

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

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

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

  • performMeasure() 来多次分发View#measure() 事件
  • performLayout() 分发 View#layout() 事件
  • performDraw() 分发 View#draw()事件

简易流程图:

image-20220420191911262

在回过头来看看文章最开始的 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 {
     ViewGroup.LayoutParams params = null;
     if (root != null) {
       // 如果 root!=null 会执行 ViewGroup#generateLayoutParams() 来初始化 params
       params = root.generateLayoutParams(attrs);
       if (!attachToRoot) {
         temp.setLayoutParams(params);
       }
     }
      // szj 正常布局执行这里 来解析xml
      rInflateChildren(parser, temp, attrs, true);
     
     // 然后会在这里ViewGroup绑定 View 和 params
     if (root != null && attachToRoot) {
       root.addView(temp, params);
     }
   }
 }

这里提一嘴merge:

image-20220420194932087

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

# 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;
                // // 如果是 ViewGroup 那么 ViewGroup 必须重写 generateLayoutParams() 才!= null
                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才能有大小!

好了,本篇到此结束!

18D066429B5C83133DD4F1135760A0CC

上一篇: android MD进阶[四] NestedScrollView 从源码到实战…

完整代码

原创不易,您的点赞就是对我最大的支持!

下集预告

CoordinatorLayout源码到实战分析

实战效果:

32295B83D78E1E3AB85F4AC8A2E30CFE
  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s10g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值