Window:每个
Activity
都会创建一个Window
用于承载View视图的显示,Window
是一个抽象类存在了一个唯一实现类PhoneWindow
- DecorView:最顶层的View,是一个
FrameLayout
子类,最终会被加载到Window当中,它内部只有一个垂直方向的LinearLayout
分为两部分:
- TitleBar:屏幕顶部的状态栏
- ContentView:
Activity
对应的XML布局,通过setContentView
设置到DecorView
中
- ActivityThread调用scheduleLaunchActivity()
- scheduleLaunchActivity发送消息sendMessage(H.LAUNCH_ACTIVITY, r);
- handleLaunchActivity初始化WindowMannagerService
- performLaunchActivity创建Activity
- 执行activity的attach
- new PhoneWindow(this,window) + 将window和windowMangerService绑定:同时,此时完成了window和activity的绑定
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// Initialize before creating the activity
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent){
//省略
Activity activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
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);
return activity;
}
final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason){
//省略
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
wm.addView(decor, l);
}
activity是系统可视化交互组件,四大组件都由AMS统一管理生命周期,事实上它的职责只是生命周期的管理,处于单一职责的原则,那势必需要将activity和其上的视图View进行解耦,那么久引入window的概念,它是个抽象类,对于activity来说,它的具体实现类是PhoneWindow,在activity执行attach的时候,会创建一个PhoneWindow对象,PhoneWindow作为装载根视图DecorView的顶级容器,activity通过setContentView实际上是调用了PhoneWindow来创建DecorView,并解析xml布局加载到DecorView的contentView部分。
了解Activity中setContentView源码:
public void setContentView(@LayoutRes int layoutResID) {
//将xml布局传递到Window当中
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
从代码可以看出,Activity
的setContentView
实质是将View
传递到Window
的setContentView()
方法中,Window
的setContenView
会在内部调用installDecor()
方法创建DecorView
,看一下它的部分源码:
public void setContentView(int layoutResID) {
...............
//初始化DecorView以及其内部的content
installDecor();
//将contentView加载到DecorVoew当中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
private void installDecor() {
...............
//实例化DecorView
mDecor = generateDecor(-1);
mDecor.setWindow(this);
//获取Content
mContentParent = generateLayout(mDecor);
}
protected DecorView generateDecor(int featureId) {
...............
return new DecorView(context, featureId, this/*PhoneWindow*/, getAttributes());
}
通过generateDecor()
new一个DecorView
,然后调用generateLayout()
获取DecorView
中content
,最终通过inflate
将Activity
视图添加到DecorView
中的content
中,但此时DecorView
还未被添加到Window
中。添加操作需要借助ViewRootImpl
。
ViewRootImpl
的作用是用来衔接WindowManager
和DecorView
,在Activity
被创建后会通过WindowManager
将DecorView
添加到PhoneWindow
中并且创建ViewRootImpl
实例,随后将DecorView
与ViewRootImpl
进行关联,最终通过执行ViewRootImpl
的performTraversals()
开启整个View树的绘制。
对应于ViewRootImpl类,它是连接WindowMannager和DecorView的纽带,View的三大流程均是通过ViewRootImpl完成的,当activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联在ViewRootImpl里面performTraversals()方法开始,从最顶层的View(ViewGroup)
开始逐层对每个View
进行绘制操作,下面来看一下该方法部分源代码:
private void performTraversals() {
...............
//measur过程
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//layout过程
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
//draw过程
performDraw();
}
对应于ViewRootImpl类,它是连接WindowMannager和DecorView的纽带,View的三大流程均是通过ViewRootImpl完成的,当activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联在ViewRootImpl里面performTraversals()分发
Android的View绘制流程是一个复杂的过程,主要包括了几个关键的步骤:Measure(测量)、Layout(布局)、Draw(绘制)。
在View的绘制流程中,首先进行的是Measure阶段。这个阶段的主要任务是确定View及其子View的大小和宽高。在源代码中,这个过程主要由onMeasure()方法完成。在这个方法中,我们需要根据MeasureSpec(它表示的是父View希望子View占据的尺寸)来确定子View的尺寸。MeasureSpec是一个int类型,包含了两个部分:大小和模式。大小表示父View希望子View占据的尺寸,模式则表示父View是如何希望我们处理这个尺寸的。
测量阶段完成后,View会得到一个大小,然后进入Layout阶段。这个阶段的主要任务是将每个View按照它们在XML文件中定义的布局进行排列。在源代码中,这个过程主要由onLayout()方法完成。在此方法中,我们需要根据View的布局参数(如宽、高、左、上、右、下的margin等)来确定View在屏幕上的具体位置。
Layout阶段完成后,每个View都会被放置在屏幕上的一个具体位置。然后进入Draw阶段,这个阶段的主要任务是将View及其子View的内容绘制到屏幕上。在源代码中,这个过程主要由onDraw()方法完成。在此方法中,我们可以在Canvas上绘制任何我们想要的内容。例如,可以在这个方法中绘制一个背景色、一个文字、一个图片等等。