From bb992b0e96dcdfa33b15e5fdbe16028927867f04 Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Wed, 16 Feb 2022 20:56:34 +0800 Subject: [PATCH 1/3] Reland "Listen for Vsync callback on the UI thread directly" --- fml/platform/android/message_loop_android.cc | 70 +++++++++++++++---- fml/platform/android/message_loop_android.h | 4 ++ .../flutter/embedding/engine/FlutterJNI.java | 9 +-- shell/platform/android/library_loader.cc | 5 ++ .../platform/android/vsync_waiter_android.cc | 12 ++-- 5 files changed, 76 insertions(+), 24 deletions(-) diff --git a/fml/platform/android/message_loop_android.cc b/fml/platform/android/message_loop_android.cc index b4c5982181a54..57652480fe562 100644 --- a/fml/platform/android/message_loop_android.cc +++ b/fml/platform/android/message_loop_android.cc @@ -13,6 +13,31 @@ namespace fml { static constexpr int kClockType = CLOCK_MONOTONIC; +static fml::jni::ScopedJavaGlobalRef* g_looper_class = nullptr; +static jmethodID g_looper_prepare_method_ = nullptr; +static jmethodID g_looper_loop_method_ = nullptr; +static jmethodID g_looper_my_looper_method_ = nullptr; +static jmethodID g_looper_quit_method_ = nullptr; + +static void LooperPrepare() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + env->CallStaticVoidMethod(g_looper_class->obj(), g_looper_prepare_method_); +} + +static void LooperLoop() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + env->CallStaticVoidMethod(g_looper_class->obj(), g_looper_loop_method_); +} + +static void LooperQuit() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + auto my_looper = env->CallStaticObjectMethod(g_looper_class->obj(), + g_looper_my_looper_method_); + if (my_looper != nullptr) { + env->CallVoidMethod(my_looper, g_looper_quit_method_); + } +} + static ALooper* AcquireLooperForThread() { ALooper* looper = ALooper_forThread(); @@ -63,23 +88,13 @@ void MessageLoopAndroid::Run() { FML_DCHECK(looper_.get() == ALooper_forThread()); running_ = true; - - while (running_) { - int result = ::ALooper_pollOnce(-1, // infinite timeout - nullptr, // out fd, - nullptr, // out events, - nullptr // out data - ); - if (result == ALOOPER_POLL_TIMEOUT || result == ALOOPER_POLL_ERROR) { - // This handles the case where the loop is terminated using ALooper APIs. - running_ = false; - } - } + LooperPrepare(); + LooperLoop(); } void MessageLoopAndroid::Terminate() { running_ = false; - ALooper_wake(looper_.get()); + LooperQuit(); } void MessageLoopAndroid::WakeUp(fml::TimePoint time_point) { @@ -93,4 +108,33 @@ void MessageLoopAndroid::OnEventFired() { } } +bool MessageLoopAndroid::Register(JNIEnv* env) { + jclass clazz = env->FindClass("android/os/Looper"); + + if (clazz == nullptr) { + return false; + } + + g_looper_class = new fml::jni::ScopedJavaGlobalRef(env, clazz); + + FML_CHECK(!g_looper_class->is_null()); + + g_looper_prepare_method_ = + env->GetStaticMethodID(g_looper_class->obj(), "prepare", "()V"); + FML_CHECK(g_looper_prepare_method_ != nullptr); + + g_looper_loop_method_ = + env->GetStaticMethodID(g_looper_class->obj(), "loop", "()V"); + FML_CHECK(g_looper_loop_method_ != nullptr); + + g_looper_my_looper_method_ = env->GetStaticMethodID( + g_looper_class->obj(), "myLooper", "()Landroid/os/Looper;"); + FML_CHECK(g_looper_my_looper_method_ != nullptr); + + g_looper_quit_method_ = + env->GetMethodID(g_looper_class->obj(), "quit", "()V"); + + return true; +} + } // namespace fml diff --git a/fml/platform/android/message_loop_android.h b/fml/platform/android/message_loop_android.h index 6ff26be0fd373..d1af3b9352be1 100644 --- a/fml/platform/android/message_loop_android.h +++ b/fml/platform/android/message_loop_android.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/message_loop_impl.h" +#include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/unique_fd.h" namespace fml { @@ -26,6 +27,9 @@ struct UniqueLooperTraits { /// This implemenation wraps usage of Android's \p looper. /// \see https://developer.android.com/ndk/reference/group/looper class MessageLoopAndroid : public MessageLoopImpl { + public: + static bool Register(JNIEnv* env); + private: fml::UniqueObject looper_; fml::UniqueFD timer_fd_; diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 128eb5c6a8e8d..41230ac273573 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -261,9 +261,10 @@ public void setRefreshRateFPS(float refreshRateFPS) { /** * The Android vsync waiter implementation in C++ needs to know when a vsync signal arrives, which - * is obtained via Java API. The delegate set here is called on the C++ side when the engine is - * ready to wait for the next vsync signal. The delegate is expected to add a postFrameCallback to - * the {@link android.view.Choreographer}, and call {@link onVsync} to notify the engine. + * is obtained via Java API. The delegate set here is called on the C++ side on the ui thread when + * the engine is ready to wait for the next vsync signal. The delegate is expected to add a + * postFrameCallback to the {@link android.view.Choreographer}, and call {@link onVsync} to notify + * the engine. * * @param delegate The delegate that will call the engine back on the next vsync signal. */ @@ -272,7 +273,7 @@ public void setAsyncWaitForVsyncDelegate(@Nullable AsyncWaitForVsyncDelegate del } // TODO(mattcarroll): add javadocs - // Called by native. + // Called by native on the ui thread. private static void asyncWaitForVsync(final long cookie) { if (asyncWaitForVsyncDelegate != null) { asyncWaitForVsyncDelegate.asyncWaitForVsync(cookie); diff --git a/shell/platform/android/library_loader.cc b/shell/platform/android/library_loader.cc index 644bd5de3b8ec..5a4d444da7309 100644 --- a/shell/platform/android/library_loader.cc +++ b/shell/platform/android/library_loader.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/fml/platform/android/jni_util.h" +#include "flutter/fml/platform/android/message_loop_android.h" #include "flutter/shell/platform/android/android_image_generator.h" #include "flutter/shell/platform/android/flutter_main.h" #include "flutter/shell/platform/android/platform_view_android.h" @@ -32,5 +33,9 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { result = flutter::AndroidImageGenerator::Register(env); FML_CHECK(result); + // Register MessageLoopAndroid. + result = fml::MessageLoopAndroid::Register(env); + FML_CHECK(result); + return JNI_VERSION_1_4; } diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index 1a69705f634d4..fa3630e08e168 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -29,13 +29,11 @@ void VsyncWaiterAndroid::AwaitVSync() { auto* weak_this = new std::weak_ptr(shared_from_this()); jlong java_baton = reinterpret_cast(weak_this); - task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() { - JNIEnv* env = fml::jni::AttachCurrentThread(); - env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), // - g_async_wait_for_vsync_method_, // - java_baton // - ); - }); + JNIEnv* env = fml::jni::AttachCurrentThread(); + env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), // + g_async_wait_for_vsync_method_, // + java_baton // + ); } // static From df7fac9698919466e6fd1c42cebe00a1bc4e8676 Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Fri, 25 Feb 2022 00:23:23 +0800 Subject: [PATCH 2/3] Tweak the code --- fml/platform/android/message_loop_android.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fml/platform/android/message_loop_android.cc b/fml/platform/android/message_loop_android.cc index 57652480fe562..16354ddaae7d1 100644 --- a/fml/platform/android/message_loop_android.cc +++ b/fml/platform/android/message_loop_android.cc @@ -88,7 +88,9 @@ void MessageLoopAndroid::Run() { FML_DCHECK(looper_.get() == ALooper_forThread()); running_ = true; + // Initialize the current thread as a looper. LooperPrepare(); + // Run the message queue in this thread. LooperLoop(); } @@ -110,13 +112,9 @@ void MessageLoopAndroid::OnEventFired() { bool MessageLoopAndroid::Register(JNIEnv* env) { jclass clazz = env->FindClass("android/os/Looper"); - - if (clazz == nullptr) { - return false; - } + FML_CHECK(!clazz->is_null()); g_looper_class = new fml::jni::ScopedJavaGlobalRef(env, clazz); - FML_CHECK(!g_looper_class->is_null()); g_looper_prepare_method_ = @@ -133,6 +131,7 @@ bool MessageLoopAndroid::Register(JNIEnv* env) { g_looper_quit_method_ = env->GetMethodID(g_looper_class->obj(), "quit", "()V"); + FML_CHECK(g_looper_quit_method_ != nullptr); return true; } From 86ed374cf9715837e9e439d914551ed82d307297 Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Fri, 25 Feb 2022 00:50:47 +0800 Subject: [PATCH 3/3] fix compile error --- fml/platform/android/message_loop_android.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fml/platform/android/message_loop_android.cc b/fml/platform/android/message_loop_android.cc index 16354ddaae7d1..79a59ba25032d 100644 --- a/fml/platform/android/message_loop_android.cc +++ b/fml/platform/android/message_loop_android.cc @@ -112,7 +112,7 @@ void MessageLoopAndroid::OnEventFired() { bool MessageLoopAndroid::Register(JNIEnv* env) { jclass clazz = env->FindClass("android/os/Looper"); - FML_CHECK(!clazz->is_null()); + FML_CHECK(clazz != nullptr); g_looper_class = new fml::jni::ScopedJavaGlobalRef(env, clazz); FML_CHECK(!g_looper_class->is_null());