本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布[2022-4-26]
android 甲骨文之 View 生命周期
前言: 最近在写materialDesign系列的博客,上一篇介绍了NestedScrollView的源码分析,本来计划本篇为CoordinatorLayout源码分析,但是CoordinatorLayout涉及到了View的生命周期的知识,我又不想潦草的糊弄,所以那就穿插一篇View的生命周期吧
源码基于: 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
流程图:
tips:
onCreate#setContentView() start
与 onCreate#setContentView() end
指的是这样
源码分析开始
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()方法中创建
-
Activity#attach() 创建 PhoneWindow()
所以这里需要关心的就是 :
- 初始化: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 事件:
wm.addView( decor , l )方法上参数为:
-
@param decor : DecorView(FrameLayout)
-
@param l : LayoutOarams(width: MATCH_PARENT, height: MATCH_PARENT)
这里的 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()
}
效果一 | 效果二 |
---|---|
![]() | ![]() |
这里比较简单,就不过多赘述了!
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
,实现类为 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()事件
简易流程图:
在回过头来看看文章最开始的 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:
这东西我是不经常用,然后查了下官方文档,看着没啥用…就是能优化一点点布局,作用不大
# 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才能有大小!
好了,本篇到此结束!
上一篇: android MD进阶[四] NestedScrollView 从源码到实战…
原创不易,您的点赞就是对我最大的支持!
下集预告
CoordinatorLayout源码到实战分析
实战效果:
![32295B83D78E1E3AB85F4AC8A2E30CFE](https://tva1.sinaimg.cn/large/e6c9d24egy1h1gge0bh2ag20a00lqb2c.gif)