android View生命周期
Posted android超级兵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android View生命周期相关的知识,希望对你有一定的参考价值。
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
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);
...
深入懂得android view 生命周期