Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 57 additions & 13 deletions fml/platform/android/message_loop_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,31 @@ namespace fml {

static constexpr int kClockType = CLOCK_MONOTONIC;

static fml::jni::ScopedJavaGlobalRef<jclass>* 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();

Expand Down Expand Up @@ -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) {
Expand All @@ -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<jclass>(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
4 changes: 4 additions & 0 deletions fml/platform/android/message_loop_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<ALooper*, UniqueLooperTraits> looper_;
fml::UniqueFD timer_fd_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,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 nativeOnVsync} 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 nativeOnVsync} to
* notify the engine.
*
* @param delegate The delegate that will call the engine back on the next vsync signal.
*/
Expand All @@ -254,7 +255,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);
Expand Down
5 changes: 5 additions & 0 deletions shell/platform/android/library_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
12 changes: 5 additions & 7 deletions shell/platform/android/vsync_waiter_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@ void VsyncWaiterAndroid::AwaitVSync() {
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
jlong java_baton = reinterpret_cast<jlong>(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
Expand Down