前言
本文结合源码分析activity页面展示的大致过程。系作者原创,转载注明出处。
从SetContentView说起
1 | /** |
主要是调用了PhoneWindow
的setContentView和初始化了ActionBar。再来看PhoneWindow
的setContentView:
主要是给当前window安装上decorView, 然后渲染layout。来看看installDecor()干了什么:
1 | private void installDecor() { |
主要是创建DecorView实例赋给mDecor,以及generateLayout传入mDecor生成mContentParent。
1 | protected DecorView generateDecor(int featureId) { |
这个方法比较长,主要是设置了window的flags和features,然后调用mDecor的onResourcesLoaded把layoutResource渲染出来并且添加为mDecor的子view,然后赋给mDecor的mContentRoot:
1 | void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { |
接着找到id为content的FrameLayout赋给mContentParent,设置mDecor的background和title等。我们来看R.layout.screen_simple的内容:
1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
可见DecorView的content root是一个LinearLayout,其中包含一个ViewStub和一个id为@android:id/content
的FrameLayout。我们用layout inspector随意打开一个activity的布局即可验证:
我们setContentView实际上就是设置了content这个id的view,但是id为action_mode_bar_stub
的viewstub是干啥的呢?以前上学时候的Android机比较常见的一个交互是在标题栏上弹出菜单项,如下:
这个action_mode_bar_stub
实际上就是这种菜单,详见:上下文菜单
到这里只是完成了DecorView的创建并初始化,什么时候添加到屏幕上展示的呢?
DecorView如何展示在用户面前
我们知道activity onResume后已经展示在前台了,那么看ActivityThread的handleResumeActivity
:
1 |
|
可知通过wm.addView(decor, l)
将decorView展示在了前台,其中l是layoutparams,其type字段为TYPE_BASE_APPLICATION
。而windowManagerImpl又通过WindowManagerGlobal调用addView:
1 |
|
WindowManagerGlobal的addView主要是创建了一个ViewRootImpl,然后将view,root,layoutparams以相同的index添加到维护的三个ArrayList中:
1 |
|
随后通过ViewRootImpl的setView方法调了一次requestLayout
,requestLayout
调用scheduleTraversals()
,往message queue中加入SyncBarrier消息,并在接下来一帧执行mTraversalRunnable
:
1 |
|
mTraversalRunnable
里做了什么呢?
1 | final class TraversalRunnable implements Runnable { |
把刚才post的SyncBarrier消息移除,然后执行performTraversals
,同时在debug模式下执行method tracing。performTraversals
方法很长,主要是执行了performMeasure
,performLayout
,performDraw
方法:
1 | ....... |
可知在performTraversal
中如果焦点因为触摸变了,或者宽高有变,或者insets有变,或者配置更新都会触发performMeasure
,而且如果设置了weight,会多执行一次performMeasure
。
1 | private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { |
接下来就是performLayout
,布局所有的view,然后计算透明区域:
1 | private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, |
然后是performDraw
,其中调用了draw
方法。这里不再细看。最后将mIsInTraversal
置为false,标记一次traversal的完成。上述即是activity页面展示的粗略过程。