前言
在了解绘制流程,首先我们需要知道什么是Vsync,没有了解的同学先去看看我的这篇文章,看了后必定如雷灌顶。
点击:2分钟带你了解什么是Vsync
回顾下这篇的内容,当程序在屏幕绘制一个画面的时候,他需要经过这样的流程:程序业务代码,经过cpu预算,计算所需要的数据,运算到gpu合成完,发送到FrameBuffer,然后被显示器读取,显示到屏幕。
有了这些概念后,我们开始探入安卓绘制流程。
安卓绘制流程
先说下思路,后面再一步步验证。我们已经知道,每当设备绘制完一帧后就会发出一个Vsync信号,而安卓的绘制流程第一步就是订阅这个信号,当需要屏幕刷新的时候(也就是你调用invalidate或requestLayout),系统就会就加入一个回调,当这个Vsync来到时候,系统就会执行这个回调,这个回调里面会将说有的视图进行业务的布局构建,然后将数据发送到gpu汇总成一个画面数据发送到缓冲区,再给显示器绘制显示出来。我们可以先粗略看下面这个图
了解视图链路
到了这里,我们已经了解大概流程,我们从视图链路层出发,认知整条连的关系,再从代码中认证。
ViewGroup和View的关系这里就不探讨了,相信大家已经清楚。从这个图我们可以发现,所有部件的顶端是一个DecorView,他的mParent和所有的不同,却是一个ViewRootImpl,并且所有子节点都可以间接访问到它。这个东西非常重要,它的作用是触发安排下一次的Vsync,然后执行回调将所有需要绘制的视图进行构建渲染!而且ViewRootImpl是每个窗口唯一,下面即将通过源码来验证这条链的关系。
我们先找到ActivityThread.java, 来到handleResumeActivity方法,这个方法主要是在Activity执行onCreate回调后,再执行onResume之前执行下面代码.这里不讨论其他的,粗略看主要代码即可,不想要看详细实现,不然我们根本看不完,看流程即可。
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
//...省略
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
//...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
//...
}
}
找到,并点进去
wm.addView(decor, l);
点进去可以发现,它是个接口,是由WindowManager继承的ViewManager的接口,他的实现类是WindowManagerImpl
因此我们去找他的实现方法addView
//....
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
/// ....
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
可见,addView并不是直接实现,由WindowManagerGlobal mGlobal代理实现,我们继续点进去看看。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
//...
ViewRootImpl root;
// ....
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession);
}
//...
mViews.add(view);//将DecorView加到窗口mViews里面
mRoots.add(root);//将ViewRootImpl加到窗口的mRoots
//...
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
//....
}
}
由此可见,ViewRootImpl 是在Acitivity的onResume 时候创建的
下一步,找到
root.setView(view, wparams, panelParentView, userId);
进去,发现他调用了requestLayout(),上面已经说过,调用这个方法会安排一个回调,下次Vsync信号回来就执行所有需要的绘制画面。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
//...
mView = view; //将DecorView保存到mView
//...
requestLayout();
//...
view.assignParent(this);
}
并将ViewRootImpl设置到DecorView,作为DecorView的mParent
void assignParent(ViewParent parent) {
if (mParent == null) {
mParent = parent;
} else if (parent == null) {
mParent = null;
} else {
throw new RuntimeException("view " + this + " being added, but"
+ " it already has a parent");
}
}
由于一个Activity只有一个Window,也就是PhoneWindow,这个PhoneWindow并不是真正意义的视图窗口,他是由DecorView来提供视图,是本窗口所有视图的最底层视图.
在ViewGroup里面,他会调用子view的assignParent,将子view的mParent指向它。我们可以看看当我们添加View时候的方法。
public void addView(View child) {
addView(child, -1);
}
public void addView(View child, int index) {
//....省略
addView(child, index, params);
}
public void addView(View child, int index, LayoutParams params) {
///....省略
addViewInner(child, index, params, false);
}
private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) {
//....省略
if (preventRequestLayout) {
child.assignParent(this);
} else {
child.mParent = this;
}
}
再回顾下上面的图,就很清晰链路上的关系。子节点有了ViewParent就可以形成一条链,需要进行刷新的时候就可以将整条链进行遍历作业,最终获取到ViewRoorImpl去 schedule一个Vsync,等待订阅的Vsync过来,然后进行构建显示到屏幕
了解Vsync来时,执行的过程
我们先快速浏览下如图相关的类,留个初步印象,方便解读
相关类图
1.与视图生命周期相关
2. 与Vysnc响应与执行相关
流程概况
先简单说下总体流程,再通过源码来验证。完成这个个关键的类主要是3个, Choreographer.java,DisplayEventReceiver.java,ViewRootImpl.java. 通过阅读我们已经了解ViewRootImpl.java与视图的关系。**ViewRootImpl.java在整个环节中,主要负责管理视图的布局与绘制业务。**其中,ViewRootImpl与Choreographer各自持有双方的对象,如图
Choreographer的作用主要是管理Vysnc的安排与接收。Choreographer只是起到一个中介的作用,真正实现是他拥有的DisplayEventReceiver这个类。
从源码角度解锁流程
第一步:初始化以及绑定Vsync消息中心
从上面我们已经知道,ViewRootImpl是在Activity添加DecorView时候创建的。而绑定Vsync的订阅消息的初始化流程是从ViewRootImpl构造方法开始,如图
ViewRootImpl
创建ViewRootImpl对象时候,构造方法获取会Choreographer对象,第一次执行时候会创建Choreographer实例。第二次后构造后(其他窗口),从Choreographer单例里面取。如下
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,boolean useSfChoreographer){
//...省略
mChoreographer = useSfChoreographer
? Choreographer.getSfInstance() : Choreographer.getInstance();
//...
}
Choreographer
Choreographer构造时候,会创建一个FrameHandler,一个FrameDisplayEventReceiver,还有一个CallbackQueue数组。其中索引为CALLBACK_TRAVERSAL的队列,用来保存绘制工作的回调,这个回调会遍历所有的子节点的布局和绘制工作,整个环境后面会详细讲到。
//...省略
public static final int CALLBACK_TRAVERSAL = 3;
public static final int CALLBACK_COMMIT = 4;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
private Choreographer(Looper looper, int vsyncSource) {
//...省略
mHandler = new FrameHandler(looper);
//...省略
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
//...省略
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
//...省略
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
FrameHandler
FrameHandler有3个消息执行处理。分别为MSG_DO_FRAME,MSG_DO_SCHEDULE_VSYNC,MSG_DO_SCHEDULE_CALLBACK
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
其中MSG_DO_FRAME消息执行doFrame,执行被绑定在每个队列中的回调,也就是执行了Choreographer.CALLBACK_TRAVERSAL的回调,也就是说这里会在UI线程中构建遍历绘制工作。
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
//...省略
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameData, frameIntervalNanos);
//...省略
}
MSG_DO_SCHEDULE_VSYNC,执行doScheduleVsync,看名字就知道,这个是用来申请Vysnc信号的。
void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}
private void scheduleVsyncLocked() {
//..
mDisplayEventReceiver.scheduleVsync(); //申请Vysnc信号
//..
}
FrameDisplayEventReceiver
这个FrameDisplayEventReceiver是真正意义的拥有申请Vsync和接收Vsync的java类。初始化时候回调用父类的Native方法,初始化Vsync的消息控制中心 NativeDisplayEventReceiver.cpp
public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) {
mMessageQueue = looper.getQueue();
//调用初始化后,会返回NativeDisplayEventReceiver.cpp这个对象指针,将这个指针保存下来,等申请Vsync的时候,需要使用这个指针
//强转成NativeDisplayEventReceiver对象。其实就是让java层帮忙保存持有这个对象
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource, eventRegistration);
}
可以找到对应的JNI,android_view_DisplayEventReceiver.cpp, 他会创建真正的NativeDisplayEventReceiver对象并初始化
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
117 jobject messageQueueObj, jint vsyncSource) {
//..
// sp这个操作符号,相当于java强引用,如果是sw<>就是虚引用
124 sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
125 receiverWeak, messageQueue, vsyncSource);
//初始化
126 status_t status = receiver->initialize();
//....
//返回NativeDisplayEventReceiver指针,让Java层实用类持有
135 return reinterpret_cast<jlong>(receiver.get());
136}
第二步: 当View绘制的时候,申请下一个Vsync进行处理
当我们需要刷新View的绘制时候,我们需要调用invalidate进行重新绘制,然后将父节点所有需要绘制的都设置绘制区域。最后invalidate会顺藤摸瓜,最终获取到ViewRootImpl对象,然后把回调塞到上面的CallbackQueue,然后申请下一个Vsync到来执行回调,遍历所有的ViewGroup和View绘制图像通过GPU发送到缓冲数据区域,被显示器读取然后显示出来。下面是具体步骤,如图
先看看invalidate这个方法,
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
// ...
//判断是否跳过重绘
//· View是不可见的
//· 当前没有设置动画
//· 父View的类型不是ViewGroup或者父ViewGoup不处于过渡态
if (skipInvalidate()) {
return;
}
// 判断是否需要重绘,如果View这些标记位,即没有变化就不会重绘
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
//加入dirty标志位,类似flutter的element被标记成dirty
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
// 设置PFLAG_INVALIDATED标志位
mPrivateFlags |= PFLAG_INVALIDATED;
// 移除PFLAG_DRAWING_CACHE_VALID标志位
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
// 设置当前需要绘制的区域,也就是View的绘制区域
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
// 将这个区域传给父节点,告诉父节点这部分区域需要绘制
p.invalidateChild(this, damage);
}
// ...
}
}
我们在看看invalidateChild,点进去发现这个是ViewParent的接口方法。
/**
* All or part of a child is dirty and needs to be redrawn.
*/
public void invalidateChild(View child, Rect r);
通过As可以找到实现的地方
转移过去后,类是ViewGroup, 我们直接跳过不相关的代码。可以看见, 这个方法一直在调用invalidateChildInParent这个方法,这个方法是由2个类实现,一个是ViewGroup,另外一个是ViewRootImpl。先说下流程,这个类主要是将父节点的所有区域进行设置相关的标志位以及对应的绘制区域,然后设置完后交给最后一个parent也就是ViewRootImpl去进行申请Vsync,因为ViewRootImpl的parent是空的,所以最后会跳出循环,理解了吧。
public final void invalidateChild(View child, final Rect dirty) {
// ...
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
// ...
parent = parent.invalidateChildInParent(location, dirty);
// ...
} while (parent != null);
}
我们先看看ViewGroup,其他代码略过,都是些设置标志位和设置绘制区域的代码。
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
// ..返回当前的parent
return mParent;
}
return null;
}
看看ViewRootImpl的invalidateChildInParent.
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
///..省略
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
//...
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
可见,ViewRootImpl最终会调用scheduleTraversals,这个方法主要是去加入Vsync回来执行的回调并且申请Vsync. 下面的重头戏到了这里就开始了。
void scheduleTraversals() {
if (!mTraversalScheduled) {
///为Vsync回来时候,执行回调时候,执行Choreographer.CALLBACK_TRAVERSAL时候,会执行到mTraversalRunnable这个方法
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
让我们看看这个有什么,他执行了doTraversal方法,这个方法属于ViewRootImpl,TraversalRunnable是ViewRootImpl的内部类。
final class TraversalRunnable implements Runnable {
@Override
public void run() {
//开始遍历布局所有相关的ViewGroup,绘制相关的View
doTraversal();
}
}
我们在将重点切回来到Choreographer的postCallback方法,doTraversal流程后面会讲到。
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
//...
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
//...
synchronized (mLock) {
//...把对于的callbackType的队列,加入回调runnable, 如Choreographer.CALLBACK_TRAVERSAL
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
// 如果是立即执行的消息
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
//延迟消息,发送一个what=MSG_DO_SCHEDULE_CALLBACK消息给FrameHandle执行
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
private final class FrameHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
//..
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
//...
}
}
最终还是回到了这里, 执行doScheduleVsync, 看名字就知道是执行申请Vsync
{
//...省略
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
//...省略
}
void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}
private static native void nativeScheduleVsync(long receiverPtr);
private void scheduleVsyncLocked() {
try {
//去申请vsync
mDisplayEventReceiver.scheduleVsync();
} finally {
//...
}
}
public void scheduleVsync() {
// 向native申请Vsync
nativeScheduleVsync(mReceiverPtr);
//...
}
如果想去深入了解Vsync,可以去framework搜索NativeDisplayEventReceiver.cpp.
如果没有源码的小伙伴,可以到这个网站http://androidxref.com/,直接搜索
android_view_DisplayEventReceiver.cpp。
这个是android_view_DisplayEventReceiver.cpp.对应的JNI ,而NativeDisplayEventReceiver.cpp则是对应的处理类。
上面初始化流程的时候,我们已经知道,java的FrameDisplayEventReceiver.java这边会持有receiverPtr这个指针,
这个指针就是NativeDisplayEventReceiver.cpp的对象指针, 通过调用reinterpret_cast就可以转换成NativeDisplayEventReceiver对象
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
//
sp<NativeDisplayEventReceiver> receiver =
reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
status_t status = receiver->scheduleVsync(); //申请Vsync
//..
}
NativeDisplayEventReceiver.cpp去调用scheduleVsync这个部分,有兴趣的去看源码。c++代码并没有java这边的代码多,梳理出来就可以看出具体流程了。主要实现类有SurfaceFlinger.cpp,DispSync.cpp, DispSyncThread.cpp,HWComposer.cpp。
第三步:执行Vsync回来后的绘制工作
到了这一步了,马上要真相大白了。相信大家通过看这个时序图就能把所有环节连在一起 了。
NativeDisplayEventReceiver的DisplayEventDispatcher中有个handleEvent方法,当接收到Vsync时候会执行到这个方法,进行分发消息。
int DisplayEventDispatcher::handleEvent(int, int events, void*) {
//...省略
//分发Vsync消息
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
//...省略
return 1; // keep the callback
}
DisplayEventDispatcher并没有实现dispatchVsync,而是交给子类NativeDisplayEventReceiver去实现,也就是调用了子类的dispatchVsync.
87void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
//下面通过JNI调用了DisplayEventReceiver.java的dispatchVsync方法
88 JNIEnv* env = AndroidRuntime::getJNIEnv();
90 ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
91 if (receiverObj.get()) {
93 env->CallVoidMethod(receiverObj.get(),
94 gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
96 }
97
98 mMessageQueue->raiseAndClearException(env, "dispatchVsync");
99}
我们再定位到DisplayEventReceiver.java,我们会发下确实有一个dispatchVsync这样的方法,注释写明了是被native调用,其实就是这个NativeDisplayEventReceiver调用。
// Called from native code.
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);
}
DisplayEventReceiver是个抽象类,具体实现是FrameDisplayEventReceiver,onVsync主要是调用Run方法再调用Choreographer的doFrame.到了这里开始真正的处理回调的事务了。开始执行布局和绘制的回调
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
//...省略
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
public void run() {
//...省略
doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
//..省略
//处理之前初始化的5个队列中加入的回调。其中CALLBACK_TRAVERSAL是UI布局和绘制相关
doCallbacks(Choreographer.CALLBACK_INPUT, frameData, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameData, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameData,
frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameData, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameData, frameIntervalNanos);
}
执行CALLBACK_TRAVERSAL的回调,其实就会执行mTraversalRunnable这个接口的run。之前是有介绍过的。mTraversalRunnable会调用ViewRootImpl的doTraversal方法。
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
//..开始执行遍历所有UI相关的构建
performTraversals();
}
}
doTraversal会调用performTraversals,这个方法主要是执行了3个步骤, 测量>布局>绘制。
是不是突然发现了新大陆,没错,执行View和ViewGroup的生命周期是在这里产生。
private void performTraversals() {
//...省略
//执行测量相关工作
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//执行布局相关工作
performLayout(lp, mWidth, mHeight);
//执行绘制相关工作
performDraw();
}
第一步:测量
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
//...省略
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
//...省略
}
}
View or ViewGroup
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//...省略
onMeasure(widthMeasureSpec, heightMeasureSpec);
//...
}
第二步:布局
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
View host=mView;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
View or ViewGroup
public void layout(int l, int t, int r, int b) {
//...省略
onLayout(changed, l, t, r, b);
}
第三步: 绘制
private boolean performDraw() {
//...省略
try {
boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);
//...省略
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {
//...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
final Canvas canvas;
//...省略
//..一个Canvas是通过Surfaceg根据绘制区域获取而来
canvas = mSurface.lockCanvas(dirty);
//...省略
//调用了View的draw
mView.draw(canvas);
}
View.java
public void draw(Canvas canvas) {
//。。。
onDraw(canvas);
}
好了到了这里,全部串通了!!!如果有帮助到你,请来个关注点赞三连吧。需要flutter 篇的小伙伴,请在评论区留下你的666。我的动力来自于你们的支持