Android screen refresh mechanism
Previously we talked about layout optimization and mentioned that the Android system sends a VSYNC signal every 16ms and then performs a UI rendering. If the rendering is successful, the interface is basically smooth.
Let’s take a look at how the Android system does the screen refresh mechanism, and how to ensure that every time we click or touch the screen, we can quickly process the corresponding events.
VSync comes from the reporting of the underlying hardware driver. For the interface visible to Android, it is an abstract hardware device from HAL layer hwC_comPOser_device
Basic knowledge of
The View map
This section was covered in a previous article on Android’s View drawing mechanism
In our previous code, there was no detailed explanation of the part 15-17, so how does the bottom layer generate the Vsync signal and then inform our application to refresh the screen? That is the focus of this article.
The entrance
mChoreographer = Choreographer.getInstance();
//Choreographer.java frameworks\base\core\java\android\view
public static Choreographer getInstance(a) {
return sThreadInstance.get();
}
private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue(a) {
// Get the corresponding looper
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
// Notice the VSYNC_SOURCE_APP used here
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
returnchoreographer; }};private Choreographer(Looper looper, int vsyncSource) {
/ / create signal is VSYNC_SOURCE_APP FrameDisplayEventReceiver, APP layer request VSYNC
mDisplayEventReceiver = USE_VSYNC? new FrameDisplayEventReceiver(looper, vsyncSource): null; . }Copy the code
Here to initialize FrameDisplayEventReceiver class inherits from DisplayEventReceiver class
public DisplayEventReceiver(Looper looper, int vsyncSource) {
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mMessageQueue = looper.getQueue();
// Call the underlying initialization and pass in itself and the corresponding mMessageQueue
/ / frameworks/base/core/jni \ android_view_DisplayEventReceiver CPP
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,vsyncSource);
mCloseGuard.open("dispose");
}
Copy the code
This calls the Native layer’s methods and passes the current DisplayEventReceiver along with the queue mMessageQueue and **vsyncSource(VSYNC_SOURCE_APP)** to the bottom layer
nativeInit
//frameworks\base\core\jni\android_view_DisplayEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, jint vsyncSource) {
// Apply for the corresponding MessageQueuesp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); ./ / create NativeDisplayEventReceiver key method 1
sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
receiverWeak, messageQueue, vsyncSource);
/ / the key methods to initialize NativeDisplayEventReceiver 2, initialization and return the corresponding results
status_t status = receiver->initialize();
if (status) {// Initialization is abnormalString8 message; led to initialize display event receiver. status message.appendFormat("Fai=%d", status);
jniThrowRuntimeException(env, message.string());
return 0;
}
receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get());
}
Copy the code
Here we see the NativeDisplayEventReceiver creation process.
[NativeDisplayEventReceiver create]
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource) :
// Inherits the DisplayEventDispatcher and passes in the corresponding Messagequeue, converting the vsyncSource to the underlying variable
DisplayEventDispatcher(messageQueue->getLooper(),
static_cast<ISurfaceComposer::VsyncSource>(vsyncSource)),
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mMessageQueue(messageQueue) {
ALOGV("receiver %p ~ Initializing display event receiver.".this);
}
//DisplayEventDispatcher constructor
DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,ISurfaceComposer::VsyncSource vsyncSource) :
// The source of Vsync is passed to the mReceiver. This is equivalent to calling the mReceiver(DisplayEventReceiver) constructor
mLooper(looper), mReceiver(vsyncSource), mWaitingForVsync(false) {
ALOGV("dispatcher %p ~ Initializing display event dispatcher.".this);
}
Copy the code
This will create a DisplayEventReceiver
//DisplayEventReceiver constructor frameworks/native/libs/GUI /DisplayEventReceiver
DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
ISurfaceComposer::ConfigChanged configChanged) {
// Get the SurfaceFling service and save it in the ComposerService
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if(sf ! =nullptr) {
/ / method 2 by binder, the last call surfaceFling createDisplayEventConnection method across processes
// Method location isurfacecomposer. CPP frameworks\native\libs\ GUI 66331 2020/3/22 1379
mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
if(mEventConnection ! =nullptr) {
3 / / method
mDataChannel = std::make_unique<gui::BitTube>();
4 / / method
mEventConnection->stealReceiveChannel(mDataChannel.get()); }}}Copy the code
The DisplayEventReceiver structure is an important class whose main purpose is to establish a connection to the SurfaceFlinger. We will do a self-study analysis of each of its methods
- Method 1: Obtain the SurfaceFlinger service
sp sf(ComposerService::getComposerService());
ComposerService::getComposerService()
// frameworks\native\libs\gui\SurfaceComposerClient.cpp
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService(a) {
ComposerService& instance = ComposerService::getInstance();
Mutex::Autolock _l(instance.mLock);/ / lock
if (instance.mComposerService == nullptr) {
// Get the SurfaceFling service and save it in the ComposerServiceComposerService::getInstance().connectLocked(); assert(instance.mComposerService ! =nullptr);
ALOGD("ComposerService reconnected");
}
return instance.mComposerService;
}
void ComposerService::connectLocked(a) {
const String16 name("SurfaceFlinger");
// Get the SurfaceFlinger service using the getService method and save the obtained service to the mComposerService variable
while(getService(name, &mComposerService) ! = NO_ERROR) { usleep(250000);
}
// Create death callback. mDeathObserver =new DeathObserver(*const_cast<ComposerService*>(this));
IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
}
Copy the code
Obtain the corresponding SurfaceFlinger service through getService method. The obtained service is saved in the mComposerService variable.
- Creating event connections
sf->createDisplayEventConnection
virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource,ConfigChanged configChanged) {
Parcel data, reply;
sp<IDisplayEventConnection> result;
/ / binder mechanism called SurfaceFling createDisplayEventConnection method
//SurfaceFlinger.cpp frameworks\native\services\surfaceflinger
int err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeInt32(static_cast<int32_t>(vsyncSource));
data.writeInt32(static_cast<int32_t>(configChanged)); err = remote()->transact( BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, data, &reply); . result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder());return result;
}
Copy the code
As you can see, the Binder mechanism is used and the provider of the service is SurfaceFlinger.
// Create a display event connection
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
//makeResyncCallback is a method defined in eventThread.h. using ResyncCallback = std::function
;
()>
// Create a resyncCallback
auto resyncCallback = mScheduler->makeResyncCallback([this] {
Mutex::Autolock lock(mStateLock);
return getVsyncPeriod();
});
// Depending on the Vsync type passed in, different handlers are returned. If registered in an application, mAppConnectionHandle is returned
const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
/ / call createDisplayEventConnection, introduced the corresponding handle, mScheduler is Scheduler. The CPP structures
return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
configChanged);
}
Copy the code
Returns a specific Handler based on the vsyncSource type passed in. Because of the type of application we used here, the Handle is mAppConnectionHandle.
The corresponding connection is then created through mScheduler.
Here we need a footnote for Handle
Supplementary notes:
The creation of the Handler is done in SurfaceFlinger’s initialization method init()
void SurfaceFlinger::init(a) {... mAppConnectionHandle = mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
mPhaseOffsets->getOffsetThresholdForNextVsync(),
resyncCallback,
impl::EventThread::InterceptVSyncsCallback()); . }sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
ResyncCallback resyncCallback,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
// The corresponding ID, cumulative
const int64_t id = sNextId++;
// Create an EventThread with the name of the incoming connectionName
std::unique_ptr<EventThread> eventThread =
makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
offsetThresholdForNextVsync, std::move(interceptCallback));
/ / create EventThreadConnection
auto eventThreadConnection = createConnectionInternal(eventThread.get(), std::move(resyncCallback),
ISurfaceComposer::eConfigChangedSuppress);
// Create ConnectionHandle with id,
// Then store the created connection into the map. The key is the id.
mConnections.emplace(id,
std::make_unique<Connection>(new ConnectionHandle(id),
eventThreadConnection,
std::move(eventThread)));
return mConnections[id]->handle;
}
Copy the code
The Handler created here holds the corresponding EventThread, and the eventThreadConnection is created through the EventThread. Once an eventThreadConnection is created, it is saved to the map and the corresponding key is the ID information.
The connection handler: ConnectionHandle is an object that holds an ID.
Let’s go back to the main story…
mScheduler->createDisplayEventConnection
// frameworks\native\services\surfaceflinger\Scheduler\Scheduler.cpp
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback,
ISurfaceComposer::ConfigChanged configChanged) {
RETURN_VALUE_IF_INVALID(nullptr);
// Handle.id is passed. Indicates whether the connection is app or surfaceFlinger
return createConnectionInternal(mConnections[handle->id]->thread.get(),
std::move(resyncCallback), configChanged);
}
sp<EventThreadConnection> Scheduler::createConnectionInternal( EventThread* eventThread, ResyncCallback&& resyncCallback, ISurfaceComposer::ConfigChanged configChanged) {
// Call the EventThread method to create the event connector
return eventThread->createEventConnection(std::move(resyncCallback), configChanged);
}
Copy the code
Let’s look at the creation of the event connector EventThreadConnection
sp<EventThreadConnection> EventThread::createEventConnection( ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
configChanged);
}
EventThreadConnection::EventThreadConnection(EventThread* eventThread,
ResyncCallback resyncCallback,
ISurfaceComposer::ConfigChanged configChanged)
: resyncCallback(std::move(resyncCallback)),
configChanged(configChanged),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
Copy the code
The most important constructor of EventThreadConnection is the creation of an mChannel, which is of type GUI ::BitTube
// frameworks\native\libs\gui\BitTube.cpp
BitTube::BitTube(size_t bufsize) {
init(bufsize, bufsize);
}
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
// Create a pair of sockets: 0 and 1, one for reading and one for writing.
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
// since we don't use the "return channel", we keep it small...
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
fcntl(sockets[0], F_SETFL, O_NONBLOCK);
fcntl(sockets[1], F_SETFL, O_NONBLOCK);
// Bind the mReceiveFd file to the socket. When Vsync arrives, mSendFd file will be used to write messages. By writing messages to the file, the Vsync signal is monitored
mReceiveFd.reset(sockets[0]);
mSendFd.reset(sockets[1]);
} else {
mReceiveFd.reset();
}
}
Copy the code
In the initialization method, a pair of sockets are created and then mReceiveFd and mSendFd are bound. When Vsync arrives, a message is written to mSendFd and the APP can then listen for changes to the file.
The onFirstRef method is automatically called when the EventThreadConnection object is created.
void EventThreadConnection::onFirstRef(a) {
mEventThread->registerDisplayEventConnection(this);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
std::lock_guard<std::mutex> lock(mMutex);
// this should never happen
auto it = std::find(mDisplayEventConnections.cbegin(),
mDisplayEventConnections.cend(), connection);
if(it ! = mDisplayEventConnections.cend()) {
ALOGW("DisplayEventConnection %p already exists", connection.get());
mCondition.notify_all(a);return ALREADY_EXISTS;
}
// Put the connection into the list of notifications.
mDisplayEventConnections.push_back(connection);
// When a new connection is made, you need to wake up the AppEventThread thread to enable Vsync.
mCondition.notify_all(a);return NO_ERROR;
}
Copy the code
The connection we created is put into mDisplayEventConnections managed by EventThread, and the AppEventThread thread is awakened to enable the Vsync signal
The whole step 2 is actually to guide the corresponding listener from APP according to the incoming vsyncSource, and then create a pair of socket connections for inter-process communication.
Let’s go back to the main line and trace
DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
ISurfaceComposer::ConfigChanged configChanged) {
// Get the SurfaceFling service and save it in the ComposerService
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if(sf ! =nullptr) {
/ / method 2 by binder, the last call surfaceFling createDisplayEventConnection method across processes
// Method location ISurfaceComposer. CPP frameworks\native\libs\ GUI
mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
if(mEventConnection ! =nullptr) {
// Method 3 gets the GUI ::BitTube object created in method 2
mDataChannel = std::make_unique<gui::BitTube>();
4 / / method
mEventConnection->stealReceiveChannel(mDataChannel.get()); }}}Copy the code
Method 3 gets the corresponding GUI ::BitTube object. Let’s focus on method four.
Method four calls The stealReceiveChannel of EventThreadConnect
status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
outChannel->setReceiveFd(mChannel.moveReceiveFd());
return NO_ERROR;
}
Copy the code
The mChannel for this is GUI ::BitTube. The Fd created in the event connector EventThreadConnection is copied to outChannel. MDataChannel for DisplayEventReceiver.
The app process has mReceivedFd and the surfaceFlinger process has mSendFd. At this time, communication can be carried out through the socket.
The purpose of the DisplayEventReceiver is to create a socket and the corresponding file, and then implement two-way communication with the SurfaceFlinger.
Here, we are so far, we just create NativeDisplayEventReceiver.
So there’s a follow-up
receiver->initialize()
status_t DisplayEventDispatcher::initialize(a) {
// Exception detection
status_t result = mReceiver.initCheck(a);if (result) {
ALOGW("Failed to initialize display event receiver, status=%d", result);
return result;
}
// Looper is the main thread Looper of the app process. This step is the BitTube channel that will be created
//fd adds listener to Looper.
int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
this.NULL);
if (rc < 0) {
return UNKNOWN_ERROR;
}
return OK;
}
Copy the code
We were able to tap in here because of our
The whole method here is simple: check for the exception and add the FD file created in Step 1 to Looper’s listener.
So far, the whole process has been worked out.
The Java layer creates a connection between the application layer and SurfaceFlinger through a pair of sockets corresponding to mReceiveFd and mSendFd, using the nativeInit function of DisplayEventReceive. The application layer adds the mReceiveFd listener to the native Looper and waits for the mSendFd to be written.
So when and how does mSendFd get written to the application layer?
While we are drawing the page refresh, how do we register for listening on Vsync
@UnsupportedAppUsage
void scheduleTraversals(a) {... mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable,null); . }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) {...// It needs to be drawn immediatelyscheduleFrameLocked(now); . }private void scheduleFrameLocked(long now) {... scheduleVsyncLocked(); . }private void scheduleVsyncLocked(a) {
// Perform synchronization and draw once. A VSYNC event listener is registered, if any
mDisplayEventReceiver.scheduleVsync();
}
public void scheduleVsync(a) {.. nativeScheduleVsync(mReceiverPtr); . }Copy the code
**nativeScheduleVsync()** is how the application layer registers with the native layer to listen for the next Vsync signal.
nativeScheduleVsync
//base\core\jni\android_view_DisplayEventReceiver.cpp 8492 2020/9/14 96
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
sp<NativeDisplayEventReceiver> receiver =
reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
// call the scheduling method from Recivier
status_t status = receiver->scheduleVsync(a); }Copy the code
The receiver, here is NativeDisplayEventReceiver. And NativeDisplayEventReceiver is inherited from DisplayEventDispatcher
DisplayEventDispatcher->scheduleVsync();
/ / scheduling Vsync
status_t DisplayEventDispatcher::scheduleVsync(a) {
// If you are currently waiting for a Vsync signal, return directly
if(! mWaitingForVsync) {nsecs_t vsyncTimestamp;
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
// Key method 1 handles the corresponding preparation event, which returns true if a Vsync signal is obtained
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "".this.ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
}
// Key method 2 requests the next Vsync signal
status_t status = mReceiver.requestNextVsync(a); .// Setup is waiting for Vsync signal
mWaitingForVsync = true;
}
return OK;
}
Copy the code
So let’s track method 1 here
DisplayEventDispatcher::processPendingEvents
bool DisplayEventDispatcher::processPendingEvents(
nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
bool gotVsync = false;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
ssize_t n;
// Get the corresponding event
while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
ALOGV("dispatcher %p ~ Read %d events.".this.int(n));
for (ssize_t i = 0; i < n; i++) {
const DisplayEventReceiver::Event& ev = buf[i];
switch (ev.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:/ / Vsync type
// Get the latest Vsync signal and save the timestamp and other information
gotVsync = true;
*outTimestamp = ev.header.timestamp;
*outDisplayId = ev.header.displayId;
*outCount = ev.vsync.count;
break; .return gotVsync;
}
Copy the code
The corresponding event type is retrieved using the getEvents method, and the Vsync signal is returned.
DisplayEventReceiver::getEvents
// native\libs\gui\DisplayEventReceiver.cpp
ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,size_t count) {
// mDataChannel is created in init to receive Vsync signals
return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
}
ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel,
Event* events, size_t count)
{
return gui::BitTube::recvObjects(dataChannel, events, count);
}
//native\libs\gui\BitTube.cpp
static ssize_t recvObjects(BitTube* tube, T* events, size_t count) {
return recvObjects(tube, events, count, sizeof(T));
}
ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) {
char* vaddr = reinterpret_cast<char*>(events);
// Read data through the socket
ssize_t size = tube->read(vaddr, count * objSize);
return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
// Read data
ssize_t BitTube::read(void* vaddr, size_t size) {
ssize_t err, len;
do {
// Place the data received by mReceiveFd into the size vaddr buffer. And returns len, the actual received data size
len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
err = len < 0 ? errno : 0;
} while (err == EINTR);
if (err == EAGAIN || err == EWOULDBLOCK) {
// If the receive is abnormal, return 0
return 0;
}
return err == 0 ? len : -err;
}
Copy the code
After the received data is put into the corresponding buffer and the data is returned, the specific data type returned is verified.
status_t DisplayEventReceiver::requestNextVsync(a) {
// Verify that the current connection exists
if(mEventConnection ! =nullptr) {
// Request the next Vsync signal through the connection. The mEventConnection. Is created when the DisplayEventReceiver is initialized
// Specify EventThreadConnection (located in EventThread)
mEventConnection->requestNextVsync(a);return NO_ERROR;
}
return NO_INIT;
}
void EventThreadConnection::requestNextVsync(a) {
ATRACE_NAME("requestNextVsync");
mEventThread->requestNextVsync(this);
}
void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
if (connection->resyncCallback) {
connection->resyncCallback(a); }// Thread locking mechanism
std::lock_guard<std::mutex> lock(mMutex);
//vsyncRequest defaults to None. Defined in the eventThread.h file
if (connection->vsyncRequest == VSyncRequest::None) {
// The reason Vsync is one-time is that we set this field to Single when we are currently None.
// This method will not be executed when the hardware receives a Vsync signal
connection->vsyncRequest = VSyncRequest::Single;
mCondition.notify_all();
}
}
Copy the code
Here, a notify_all() is called when a Vsync signal arrives. This method wakes up all threads that have performed the **wait()** method.
So who will this wake up?
This brings us to the EventThread creation process.
EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
: mVSyncSource(src),
mVSyncSourceUnique(std::move(uniqueSrc)),
mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
mThreadName(threadName) {
...
// Create mThread thread
mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
std::unique_lock<std::mutex> lock(mMutex);
The threadMain function is called when the thread is created
threadMain(lock); }); . }Copy the code
When An EventThread is created, a thread is created and the threadMain method is called.
// This method is called when EventThread is created. It's going to iterate over and over again
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
// Keep iterating through the loop as long as there is no exit
while(mState ! = State::Quit) { std::optional<DisplayEventReceiver::Event> event; .// Whether there is a Vsync request
bool vsyncRequested = false; .// query all connections, actually a connection is a listener
auto it = mDisplayEventConnections.begin(a);while(it ! = mDisplayEventConnections.end()) {
if (const auto connection = it->promote()) { vsyncRequested |= connection->vsyncRequest ! = VSyncRequest::None;// traverse, putting the listeners that need to be notified into consumers
if (event && shouldConsumeEvent(*event, connection)) {
consumers.push_back(connection);
}
++it;
} else {
it = mDisplayEventConnections.erase(it); }}if(! consumers.empty()) {
// Perform event distribution. Will call GUI: : BitTube: : sendObjects function
dispatchEvent(*event, consumers);
consumers.clear(a); } State nextState;if (mVSyncState && vsyncRequested) {
nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
} else {
ALOGW_IF(! mVSyncState,"Ignoring VSYNC request while display is disconnected");
nextState = State::Idle;
}
if(mState ! = nextState) {if (mState == State::VSync) {
mVSyncSource->setVSyncEnabled(false);
} else if (nextState == State::VSync) {
mVSyncSource->setVSyncEnabled(true);
}
mState = nextState;
}
if (event) {
continue;
}
// Wait for the event request
if (mState == State::Idle) {
mCondition.wait(lock);
} else{... }}}Copy the code
The threadMain function loops over and over again. If an EventThreadConnection is found that can consume the event, dispatchEvent is called to distribute the event. If the current state is idle, the thread is put into wait, waiting to wake up.
That’s what we called arousal earlier.
When a Vsync signal arrives, the dispatchEvent method is called for distribution
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
const DisplayEventConsumers& consumers) {
// Where DisplayEventConsumers is a vector and holds an EventThreadConnection internally.
for (const auto& consumer : consumers) {
switch (consumer->postEvent(event)) {
case NO_ERROR:
break;
case -EAGAIN:
ALOGW("Failed dispatching %s for %s".toString(event).c_str(),
toString(*consumer).c_str());
break;
default:
// Treat EPIPE and other errors as fatal.
removeDisplayEventConnectionLocked(consumer); }}}Copy the code
Let’s look at the postEvent method
//EventThread.cpp frameworks\native\services\surfaceflinger\Scheduler
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
return size < 0 ? status_t(size) : status_t(NO_ERROR);
}
//DisplayEventReceiver.cpp frameworks\native\libs\gui
ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
Event const* events, size_t count)
{
return gui::BitTube::sendObjects(dataChannel, events, count);
}
ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
Event const* events, size_t count)
{
// The Vsync signal is sent to the corresponding BitTube
return gui::BitTube::sendObjects(dataChannel, events, count);
}
//BitTube.h frameworks\native\libs\gui\include\private\gui
static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) {
return sendObjects(tube, events, count, sizeof(T));
}
ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
const char* vaddr = reinterpret_cast<const char*>(events);
// Write data to vaddr. When mSendFd writes to the file, the corresponding mReceiveFd receives the data.
// Then mReceiveFd calls the corresponding callback function
ssize_t size = tube->write(vaddr, count * objSize); .return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
Copy the code
When sendObjects writes to mSendFd, mReceiveFd can receive the message. In nativeInit, mReceiveFd is added to the Handler’s epoll for listening. So when data is written, the corresponding handleEvent callback is called. This callback is registered with mReceiveFd when it is added
The callback process
//mReceiveFd can receive the written data and then call this method.
int DisplayEventDispatcher::handleEvent(int.int events, void*) {
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
ALOGE("Display event receiver pipe was closed or an error occurred. "
"events=0x%x", events);
return 0; // remove the callback
}
nsecs_t vsyncTimestamp;
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%"
ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d".this.ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
// A Vsync message has been obtained here, so it will be waiting for the Vsync flag position to be false.
mWaitingForVsync = false;
// Make a distribution. The concrete is now DisplayEventDispater (android_view_DisplayEventReceiver) defined in subclasses NativeDisplayEventReceiver
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
}
return 1; // keep the callback
}
//android_view_DisplayEventReceiver.cpp frameworks\base\core\jni
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
uint32_t count) {
// The JNI context
JNIEnv* env = AndroidRuntime::getJNIEnv(a);// Where mReceiverWeakGlobal
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.".this);
// The JNI method is used to call dispatchVsync with the corresponding timestamp, display screen and the number of vsyncs
// Is actually the dispatchVsync method of the DisplayEventReceiver
env->CallVoidMethod(receiverObj.get(),
gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
ALOGV("receiver %p ~ Returned from vsync handler.".this);
}
mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}
Copy the code
Our dispatchVsync method in Java will eventually be called.
//DisplayEventReceiver.java frameworks\base\core\java\android\view
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
onVsync(timestampNanos, physicalDisplayId, frame);
}
Copy the code
Finally back to our main line…
So we’ve got the underlined part. The rest of the Java layer callback processing, we explained in the previous View drawing, if you are interested.
reference
Dandanlove.com/2018/04/25/…
Blog.csdn.net/stven_king/…
Blog.csdn.net/litefish/ar…
Android VSync signal generation and propagation structure details
Blog.csdn.net/qq_34211365…
Blog.csdn.net/qq_34211365…
This article is published by Kaiken!
Synchronous public number [open Ken]