当前位置:网站首页>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 .
边栏推荐
- 当他们在私域里,掌握了分寸感
- When an interface has an exception, how do you analyze the exception?
- 转行软件测试2年了,给还在犹豫的女生一点建议
- Smartctl opens the device and encounters permission denied problem troubleshooting process record
- ProcessOn制作ER过程(自定义)
- qt打包exe文件,解决“无法定位程序输入点_ZdaPvj于动态链接库Qt5Cored.dll”
- 3年测试经验,连简历上真正需要什么都没搞明白,张口就要20k?
- Processon producer process (customized)
- Modifying universal render data at runtime
- C#实现水晶报表绑定数据并实现打印
猜你喜欢
随机推荐
PSQL column to row
|How to analyze bugs? Professional summary and analysis
Can automate - 10k, can automate - 20K, do you understand automated testing?
Once beego failed to find bee after passing the go get command Exe's pit
请问polarDB数据库可以通过mysql进行数据源连接吗
常用的软件测试工具清单,请查收。
业务与技术双向结合构建银行数据安全管理体系
Are programmers from Huawei, Alibaba and other large manufacturers really easy to find?
計網 | 【四 網絡層】知識點及例題
F - Spices(线性基)
LeetCode 210:课程表 II (拓扑排序)
Once beego failed to find bee after passing the go get command Exe's pit
[STL source code analysis] configurator (to be supplemented)
Charles packet capturing tool
Mall project pc--- product details page
如何卸载cuda
When an interface has an exception, how do you analyze the exception?
Four characteristics of actual attack and defense drill
ERROR日志格式与注意点
Uncaught Error: [About] is not a <Route> component. All component children of <Routes> must be a <Ro