本文中的源码是基于API29的,即Android11系统版本
先看一个简单的布局。
布局查看工具:Tools–>Layout Inspector
通过:Android\Sdk\tools\bin\uiautomatorviewer.bat查看布局
其中这个android:id/content的FrameLayout就是Activity中setContentView(R.layout.activity_main)的父布局
通过Hierarchy Viewer(布局树查看器)
- DecorView是根布局
- ContentFrameLayout是Activity中setContentView(R.layout.activity_main)的父布局
通过前面的图片,我们知道了View家族的结构。而View绘制就是从DecorView跟布局开始。
在自定义View的时候一般需要重写父类的onMeasure()、onLayout()、onDraw()三个方法,来完成视图的展示过程。当然更复杂的工作都要系统代劳了,自定义的工作相对比较简单。一个完整的绘制流程是要包含measure、layout、draw这三个步骤的。
measure:测量。系统会先根据xml布局文件和代码中对控件属性的设置,来获取或者计算出每个View和ViewGrop的尺寸,并将这些尺寸保存下来。
layout:布局。根据测量出的结果以及对应的参数,来确定每一个控件应该显示的位置。
draw:绘制。确定好位置后,就将这些控件绘制到屏幕上。
本篇文章只讲绘制流程,具体怎么测量,怎么布局,怎么绘制在后续的文章中会在讲
1
//ActivityThread.java
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {
unscheduleGcIdler();
...
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
wm.addView(decor, l);
...
}
这里的decor就是前文说的DecorView;ViewManager是WindowManagerImpl;
在Activity执行performResumeActivity以后,会执行ViewManager.addView()方法,将DecorView添加到Window上。由此可以得到:View的绘制是在Activity onResume()之后进行的。
2
//WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
...
ViewRootImpl root;
...
root = new ViewRootImpl(view.getContext(), display);
...
root.setView(view, wparams, panelParentView);
...
3
//ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
...
requestLayout();
...
}
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
//这个方法大家应该比较熟悉,当我们在子线程更新UI的时候就会由这个方法抛出异常
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
checkThread这个方法我想大家应该很熟悉,开发中被它奇袭过很多次。
4
这个方法很重要,这里开始的时候发送了一个屏障方法,发送一个铜须消息,这个时候MessageQueue中的异步消息就不能处理,被阻塞掉,只有屏障消息被移除后才能处理其他消息。
屏障消息可以欢迎查看文章Andriod-消息机制Handler
//ViewRootImpl.java
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //设置屏障消息
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //发送同步消息
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //取消屏障消息
mChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //移除消息
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
5
performTraversals这个方法非常长,有800行左右,我们只关注重要逻辑,有感兴趣的小伙伴可以去细研究。
void doTraversal() {
...
performTraversals();
...
}
private void performTraversals() {
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //测量
...
performLayout(lp, mWidth, mHeight); //布局
...
performDraw(); //绘制
...
}
6
//ViewRootImpl.java
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
//ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
...
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}