15分钟带你彻底了解App绘制流程-安卓篇
前言
在了解绘制流程,首先我们需要知道什么是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消息中心
第二步: 当View绘制的时候,请求安排下一个Vsync进行处理
第三步:执行Vsync回来后的绘制工作
未编辑完,待更新
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!