/ 今日科技快讯 /
近日,浙江天猫技术有限公司、淘宝(中国)软件有限公司发生工商变更,董事长/总经理更新为淘宝天猫业务负责人戴珊;财务董事更新为李发光。此外,阿里巴巴网络科技有限公司财务董事已经由武卫更新为徐宏。
据悉,2021年12月6日,阿里巴巴集团董事长张勇发布全员信,任命戴珊代表集团分管中国数字商业板块;4月1日起,武卫不再担任阿里巴巴集团CFO,由徐宏接任。据了解,此次一系列变更是出于权责一致的法务安排。
/ 作者简介 /
本篇文章来自android超级兵的投稿,文章主要分享了他对View生命周期的探索分析,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。
android超级兵的博客地址:
https://blog.csdn.net/weixin_44819566?type=blog
/ 前言 /
最近在写materialDesign系列的博客,本来计划本篇为CoordinatorLayout源码分析,但是CoordinatorLayout涉及到了View的生命周期的知识,我又不想潦草的糊弄,所以那就穿插一篇View的生命周期吧。
源码基于: 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
流程图
tips
onCreate#setContentView()中的start 与 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()
}
这里比较简单,就不过多赘述了!
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() 事件
简易流程图:
在回过头来看看文章最开始的 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:
这东西我是不经常用,然后查了下官方文档,看着没啥用…就是能优化一点点布局,作用不大。
# 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才能有大小!
好了,本篇到此结束!
推荐阅读:
Android嵌套滑动,我用NestedScrollView
欢迎关注我的公众号
学习技术或投稿
长按上图,识别图中二维码即可关注