From a67a3664528b03b6d474be463d5d2163bae2a134 Mon Sep 17 00:00:00 2001 From: ColdPaleLight <31977171+ColdPaleLight@users.noreply.github.com> Date: Fri, 25 Feb 2022 01:51:05 +0800 Subject: [PATCH] Reland "Listen for Vsync callback on the UI thread directly" (#31494) --- fml/platform/android/message_loop_android.cc | 69 +++++++++++++++---- 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, 75 insertions(+), 24 deletions(-) diff --git a/fml/platform/android/message_loop_android.cc b/fml/platform/android/message_loop_android.cc index b4c5982181a54..79a59ba25032d 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,15 @@ 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; - } - } + // Initialize the current thread as a looper. + LooperPrepare(); + // Run the message queue in this thread. + LooperLoop(); } void MessageLoopAndroid::Terminate() { running_ = false; - ALooper_wake(looper_.get()); + LooperQuit(); } void MessageLoopAndroid::WakeUp(fml::TimePoint time_point) { @@ -93,4 +110,30 @@ void MessageLoopAndroid::OnEventFired() { } } +bool MessageLoopAndroid::Register(JNIEnv* env) { + jclass clazz = env->FindClass("android/os/Looper"); + FML_CHECK(clazz != nullptr); + + 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"); + FML_CHECK(g_looper_quit_method_ != nullptr); + + 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