当前位置:网站首页>Refresh mechanism of vie
Refresh mechanism of vie
2022-06-25 02:40:00 【Dongdongxu Huster】
Preface
Usually, I only know that I am calling invalidate, requestLayout, perhaps The screen can be refreshed through animation , Achieve what you want ui effect , But yes. view I don't understand the refresh mechanism of , This paper records my thinking and summary on the following issues .
- view How often do I refresh
- view When to refresh
- view How to refresh
- If the interface doesn't change , Do you still need to refresh
- Just call invalidate, requestLayout Wait for the function to be refreshed immediately
view How often do I refresh
Android The system every 16ms Will be issued VSYNC Signal redraw our interface (Activity).
Why 16ms, because Android The set refresh rate is 60FPS(Frame Per Second), That is, every second 60 Refresh rate of frame , About us 16ms Refresh once .
For a system , Can be divided into CPU,GPU And the display ,CPU Responsible for calculation ,GPU Render the calculated data , Then put it in the buffer and save it , The display takes the rendered data at a fixed frequency and displays it . The display refresh rate is fixed , however CPU and GPU The calculation and rendering time is irregular , hypothesis GPU The rendering rate is instantaneous , The main time factor depends on CPU,CPU The process of calculation is actually View Tree drawing process , That is, start from the root layout , Traverse all view Perform measurements separately 、 Layout 、 The process of drawing , If our interface is too complex , stay 16ms The calculation is not completed within , What the monitor gets is old data , This is the frame drop , For users, they will feel stuck .

The figure above shows a typical frame dropping process .
view Refresh time
We all know invalidate(), requestLayout() The function makes view Redraw , But is redrawing started as soon as it is called ? As mentioned above VSYNC What does the signal do ?
We from View Of invalidate Function to see the whole execution process
public void invalidate(Rect dirty) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
dirty.right - scrollX, dirty.bottom - scrollY, true, false);
}
invalidateInternal() Function will be called recursively parent Of invaldateChild() function , Will eventually be called to ViewRootImpl Of invalidate() Method
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// Insert a synchronous message barrier
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
mTraversalRunnable It's a Runnable object , It internally executes doTraversal() Method , In this method, the traversal and drawing operations are really started , As you can see from the above code , It doesn't start drawing immediately , But through mChoreographer Object registers a callback , In this postCallback Synchronization will be requested in VSYNC Information .
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
// Apply for synchronization signal
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
Because the synchronization message barrier is added , So here we create an asynchronous MSG_DO_FRAME news , Here is a very important class FrameDisplayEventReceiver
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
private VsyncEventData mLastVsyncEventData = new VsyncEventData();
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, 0);
}
// TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
// the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
// for the internal display implicitly.
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"Choreographer#onVsync " + vsyncEventData.id);
}
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
// earlier than the frame time, then the vsync event will be processed immediately.
// Otherwise, messages that predate the vsync event will be handled first.
long now = System.nanoTime();
if (timestampNanos > now) {
Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
+ " ms in the future! Check that graphics HAL is generating vsync "
+ "timestamps using the correct timebase.");
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
mLastVsyncEventData = vsyncEventData;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}
}
This class will receive the underlying VSYNC The signal ,VSync The signal is from SurfaceFlinger Realize and send regularly ,FrameDisplayEventReceiver After receiving the signal , call onVsync Method organizes messages sent to the main thread for processing . The main content of this message is run Method doFrame() Method . in other words , We must register in advance , At the bottom VSYNC Only when the signal is generated can it be recalled to our app in , Otherwise, the signal will not be received .
So , When we call invalidate(),requestLayout(), And so on , These refresh operations will not be performed immediately , But through ViewRootImpl Of scheduleTraversals() First register with the bottom layer to listen for the next screen refresh signal event , Then when the next screen refresh signal comes , Will go through performTraversals() Ergodic rendering View Tree to perform these refresh operations .
summary
Sum up , It can be concluded that
- Only synchronization signals VSYNC The interface will be refreshed when it arrives
- UI If there is no change , The synchronization signal is not requested , The interface doesn't refresh
- Synchronous signal VSYNC You need to apply to have .
- In the same frame , If there are multiple redrawing requests ,scheduleTraversals() Will filter it out , You only need to schedule a drawing task , The next time VSYNC Only when the signal arrives will it call **performTraversals()** Traverse view Tree union redrawing .
- In order to ensure that the drawing task is performed first ,ViewRootImpl A synchronization message barrier will be inserted , Synchronization messages will not be processed , So as to ensure as much as possible that VSYNC The signal can be processed at the first time .
边栏推荐
- Yarn: unable to load file c:\users\xxx\appdata\roaming\npm\yarn PS1 because running scripts is prohibited on this system
- How transformers Roberta adds tokens
- 【FPGA】串口以命令控制温度采集
- Jetson Nano 从入门到实战(案例:Opencv配置、人脸检测、二维码检测)
- mysql命令备份
- UnityShader入门精要——表面着色器
- |遇到bug怎么分析,专业总结分析来了
- Experience of epidemic prevention and control, home office and online teaching | community essay solicitation
- 使用ShaderGraph制作边缘融合粒子Shader的启示
- npm包发布详细教程
猜你喜欢

Please run IDA with elevated permissons for local debugging.

李宏毅《机器学习》丨6. Convolutional Neural Network(卷积神经网络)

Leecode learning notes - the shortest path for a robot to reach its destination

Application of TSDB in civil aircraft industry

Random list random generation of non repeating numbers

Pit entry machine learning: I. Introduction

背了八股文,六月赢麻了……

【STL源码剖析】STL六大组件功能与运用(目录)

【Proteus仿真】Arduino UNO+数码管显示4x4键盘矩阵按键

记一次beego通过go get命令后找不到bee.exe的坑
随机推荐
Intranet learning notes (6)
F - Spices(线性基)
yarn : 无法加载文件 C:\Users\xxx\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本
Once beego failed to find bee after passing the go get command Exe's pit
qt打包exe文件,解决“无法定位程序输入点_ZdaPvj于动态链接库Qt5Cored.dll”
Pytorch learning notes (VII) ------------------ vision transformer
[i.mx6ul] u-boot migration (VI) network driver modification lan8720a
目录权限错误导致 Oracle 11g rac 集群数据库无法启动的问题
|遇到bug怎么分析,专业总结分析来了
|How to analyze bugs? Professional summary and analysis
【Proteus仿真】Arduino UNO+数码管显示4x4键盘矩阵按键
It's 2022, and you still don't know what performance testing is?
Planification du réseau | [quatre couches de réseau] points de connaissance et exemples
Centos7.3 modifying MySQL default password_ Explain centos7 modifying the password of the specified user in MySQL
How to uninstall CUDA
Are programmers from Huawei, Alibaba and other large manufacturers really easy to find?
对进程内存的实践和思考
Is the compass reliable? Is it safe to open a securities account?
Enlightenment of using shadergraph to make edge fusion particle shader
Of the seven levels of software testers, it is said that only 1% can achieve level 7