Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 9be4b8b

Browse files
authored
Listen for Vsync callback on the UI thread directly (#29889)
1 parent f31be87 commit 9be4b8b

5 files changed

Lines changed: 76 additions & 24 deletions

File tree

fml/platform/android/message_loop_android.cc

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,31 @@ namespace fml {
1313

1414
static constexpr int kClockType = CLOCK_MONOTONIC;
1515

16+
static fml::jni::ScopedJavaGlobalRef<jclass>* g_looper_class = nullptr;
17+
static jmethodID g_looper_prepare_method_ = nullptr;
18+
static jmethodID g_looper_loop_method_ = nullptr;
19+
static jmethodID g_looper_my_looper_method_ = nullptr;
20+
static jmethodID g_looper_quit_method_ = nullptr;
21+
22+
static void LooperPrepare() {
23+
JNIEnv* env = fml::jni::AttachCurrentThread();
24+
env->CallStaticVoidMethod(g_looper_class->obj(), g_looper_prepare_method_);
25+
}
26+
27+
static void LooperLoop() {
28+
JNIEnv* env = fml::jni::AttachCurrentThread();
29+
env->CallStaticVoidMethod(g_looper_class->obj(), g_looper_loop_method_);
30+
}
31+
32+
static void LooperQuit() {
33+
JNIEnv* env = fml::jni::AttachCurrentThread();
34+
auto my_looper = env->CallStaticObjectMethod(g_looper_class->obj(),
35+
g_looper_my_looper_method_);
36+
if (my_looper != nullptr) {
37+
env->CallVoidMethod(my_looper, g_looper_quit_method_);
38+
}
39+
}
40+
1641
static ALooper* AcquireLooperForThread() {
1742
ALooper* looper = ALooper_forThread();
1843

@@ -63,23 +88,13 @@ void MessageLoopAndroid::Run() {
6388
FML_DCHECK(looper_.get() == ALooper_forThread());
6489

6590
running_ = true;
66-
67-
while (running_) {
68-
int result = ::ALooper_pollOnce(-1, // infinite timeout
69-
nullptr, // out fd,
70-
nullptr, // out events,
71-
nullptr // out data
72-
);
73-
if (result == ALOOPER_POLL_TIMEOUT || result == ALOOPER_POLL_ERROR) {
74-
// This handles the case where the loop is terminated using ALooper APIs.
75-
running_ = false;
76-
}
77-
}
91+
LooperPrepare();
92+
LooperLoop();
7893
}
7994

8095
void MessageLoopAndroid::Terminate() {
8196
running_ = false;
82-
ALooper_wake(looper_.get());
97+
LooperQuit();
8398
}
8499

85100
void MessageLoopAndroid::WakeUp(fml::TimePoint time_point) {
@@ -93,4 +108,33 @@ void MessageLoopAndroid::OnEventFired() {
93108
}
94109
}
95110

111+
bool MessageLoopAndroid::Register(JNIEnv* env) {
112+
jclass clazz = env->FindClass("android/os/Looper");
113+
114+
if (clazz == nullptr) {
115+
return false;
116+
}
117+
118+
g_looper_class = new fml::jni::ScopedJavaGlobalRef<jclass>(env, clazz);
119+
120+
FML_CHECK(!g_looper_class->is_null());
121+
122+
g_looper_prepare_method_ =
123+
env->GetStaticMethodID(g_looper_class->obj(), "prepare", "()V");
124+
FML_CHECK(g_looper_prepare_method_ != nullptr);
125+
126+
g_looper_loop_method_ =
127+
env->GetStaticMethodID(g_looper_class->obj(), "loop", "()V");
128+
FML_CHECK(g_looper_loop_method_ != nullptr);
129+
130+
g_looper_my_looper_method_ = env->GetStaticMethodID(
131+
g_looper_class->obj(), "myLooper", "()Landroid/os/Looper;");
132+
FML_CHECK(g_looper_my_looper_method_ != nullptr);
133+
134+
g_looper_quit_method_ =
135+
env->GetMethodID(g_looper_class->obj(), "quit", "()V");
136+
137+
return true;
138+
}
139+
96140
} // namespace fml

fml/platform/android/message_loop_android.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "flutter/fml/macros.h"
1313
#include "flutter/fml/message_loop_impl.h"
14+
#include "flutter/fml/platform/android/jni_util.h"
1415
#include "flutter/fml/unique_fd.h"
1516

1617
namespace fml {
@@ -26,6 +27,9 @@ struct UniqueLooperTraits {
2627
/// This implemenation wraps usage of Android's \p looper.
2728
/// \see https://developer.android.com/ndk/reference/group/looper
2829
class MessageLoopAndroid : public MessageLoopImpl {
30+
public:
31+
static bool Register(JNIEnv* env);
32+
2933
private:
3034
fml::UniqueObject<ALooper*, UniqueLooperTraits> looper_;
3135
fml::UniqueFD timer_fd_;

shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,10 @@ public void setRefreshRateFPS(float refreshRateFPS) {
243243

244244
/**
245245
* The Android vsync waiter implementation in C++ needs to know when a vsync signal arrives, which
246-
* is obtained via Java API. The delegate set here is called on the C++ side when the engine is
247-
* ready to wait for the next vsync signal. The delegate is expected to add a postFrameCallback to
248-
* the {@link android.view.Choreographer}, and call {@link nativeOnVsync} to notify the engine.
246+
* is obtained via Java API. The delegate set here is called on the C++ side on the ui thread when
247+
* the engine is ready to wait for the next vsync signal. The delegate is expected to add a
248+
* postFrameCallback to the {@link android.view.Choreographer}, and call {@link nativeOnVsync} to
249+
* notify the engine.
249250
*
250251
* @param delegate The delegate that will call the engine back on the next vsync signal.
251252
*/
@@ -254,7 +255,7 @@ public void setAsyncWaitForVsyncDelegate(@Nullable AsyncWaitForVsyncDelegate del
254255
}
255256

256257
// TODO(mattcarroll): add javadocs
257-
// Called by native.
258+
// Called by native on the ui thread.
258259
private static void asyncWaitForVsync(final long cookie) {
259260
if (asyncWaitForVsyncDelegate != null) {
260261
asyncWaitForVsyncDelegate.asyncWaitForVsync(cookie);

shell/platform/android/library_loader.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
#include "flutter/fml/platform/android/jni_util.h"
6+
#include "flutter/fml/platform/android/message_loop_android.h"
67
#include "flutter/shell/platform/android/android_image_generator.h"
78
#include "flutter/shell/platform/android/flutter_main.h"
89
#include "flutter/shell/platform/android/platform_view_android.h"
@@ -32,5 +33,9 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
3233
result = flutter::AndroidImageGenerator::Register(env);
3334
FML_CHECK(result);
3435

36+
// Register MessageLoopAndroid.
37+
result = fml::MessageLoopAndroid::Register(env);
38+
FML_CHECK(result);
39+
3540
return JNI_VERSION_1_4;
3641
}

shell/platform/android/vsync_waiter_android.cc

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,11 @@ void VsyncWaiterAndroid::AwaitVSync() {
2929
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
3030
jlong java_baton = reinterpret_cast<jlong>(weak_this);
3131

32-
task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() {
33-
JNIEnv* env = fml::jni::AttachCurrentThread();
34-
env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), //
35-
g_async_wait_for_vsync_method_, //
36-
java_baton //
37-
);
38-
});
32+
JNIEnv* env = fml::jni::AttachCurrentThread();
33+
env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), //
34+
g_async_wait_for_vsync_method_, //
35+
java_baton //
36+
);
3937
}
4038

4139
// static

0 commit comments

Comments
 (0)