From 4f56484da5c6f267d974a1903ba32a6665a0c31e Mon Sep 17 00:00:00 2001 From: Jia Hao Goh Date: Wed, 29 Jun 2022 13:59:50 +0800 Subject: [PATCH 1/2] Avoid vsync scheduling delay --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/android/BUILD.gn | 1 + .../engine/dart/PlatformTaskQueue.java | 5 ++- .../engine/loader/FlutterLoader.java | 3 +- .../io/flutter/util/HandlerCompat.java | 35 +++++++++++++++++++ tools/android_lint/project.xml | 1 + 6 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 shell/platform/android/io/flutter/util/HandlerCompat.java diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e02a6409acf08..9494862af2ea8 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1483,6 +1483,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platfor FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java +FILE: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/Preconditions.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/Predicate.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 84827f4610e77..c727e4f34371f 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -288,6 +288,7 @@ android_java_sources = [ "io/flutter/plugin/platform/PlatformViewsController.java", "io/flutter/plugin/platform/SingleViewPresentation.java", "io/flutter/plugin/platform/VirtualDisplayController.java", + "io/flutter/util/HandlerCompat.java", "io/flutter/util/PathUtils.java", "io/flutter/util/Preconditions.java", "io/flutter/util/Predicate.java", diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformTaskQueue.java b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformTaskQueue.java index e90ab89e62223..82d022a43907d 100644 --- a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformTaskQueue.java +++ b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformTaskQueue.java @@ -7,10 +7,13 @@ import android.os.Handler; import android.os.Looper; import androidx.annotation.NonNull; +import io.flutter.util.HandlerCompat; /** A BinaryMessenger.TaskQueue that posts to the platform thread (aka main thread). */ public class PlatformTaskQueue implements DartMessenger.DartMessengerTaskQueue { - @NonNull private final Handler handler = new Handler(Looper.getMainLooper()); + // Use an async handler because the default is subject to vsync synchronization and can result + // in delays when dispatching tasks. + @NonNull private final Handler handler = HandlerCompat.createAsyncHandler(Looper.getMainLooper()); @Override public void dispatch(@NonNull Runnable runnable) { diff --git a/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index 629984600d758..b09be89e4b57f 100644 --- a/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -23,6 +23,7 @@ import io.flutter.FlutterInjector; import io.flutter.Log; import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.util.HandlerCompat; import io.flutter.util.PathUtils; import io.flutter.util.TraceSection; import io.flutter.view.VsyncWaiter; @@ -385,7 +386,7 @@ public void ensureInitializationCompleteAsync( Log.e(TAG, "Flutter initialization failed.", e); throw new RuntimeException(e); } - new Handler(Looper.getMainLooper()) + HandlerCompat.createAsyncHandler(Looper.getMainLooper()) .post( () -> { ensureInitializationComplete(applicationContext.getApplicationContext(), args); diff --git a/shell/platform/android/io/flutter/util/HandlerCompat.java b/shell/platform/android/io/flutter/util/HandlerCompat.java new file mode 100644 index 0000000000000..de391419a9abd --- /dev/null +++ b/shell/platform/android/io/flutter/util/HandlerCompat.java @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.util; + +import android.os.Build; +import android.os.Handler; +import android.os.Looper; + +/** Compatability wrapper over {@link Handler}. */ +public final class HandlerCompat { + /** + * Create a new Handler whose posted messages and runnables are not subject to synchronization + * barriers such as display vsync. + * + *

Messages sent to an async handler are guaranteed to be ordered with respect to one another, + * but not necessarily with respect to messages from other Handlers. Compatibility behavior: + * + *

+ * + * @param looper the Looper that the new Handler should be bound to + * @return a new async Handler instance + * @see Handler#createAsync(Looper) + */ + public static Handler createAsyncHandler(Looper looper) { + if (Build.VERSION.SDK_INT >= 28) { + return Handler.createAsync(looper); + } + return new Handler(looper); + } +} diff --git a/tools/android_lint/project.xml b/tools/android_lint/project.xml index 583b66491985d..1b1c2d514f6ef 100644 --- a/tools/android_lint/project.xml +++ b/tools/android_lint/project.xml @@ -23,6 +23,7 @@ + From 4427383c87118f2a4809e4b126d0b9bc302cfae1 Mon Sep 17 00:00:00 2001 From: Jia Hao Goh Date: Thu, 30 Jun 2022 11:56:55 +0800 Subject: [PATCH 2/2] Add a test for HandlerCompat --- .../io/flutter/util/HandlerCompatTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 shell/platform/android/test/io/flutter/util/HandlerCompatTest.java diff --git a/shell/platform/android/test/io/flutter/util/HandlerCompatTest.java b/shell/platform/android/test/io/flutter/util/HandlerCompatTest.java new file mode 100644 index 0000000000000..df1352aee666f --- /dev/null +++ b/shell/platform/android/test/io/flutter/util/HandlerCompatTest.java @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.util; + +import static org.junit.Assert.assertTrue; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +@RunWith(RobolectricTestRunner.class) +public class HandlerCompatTest { + @Test + @Config(sdk = 28) + public void createAsync_createsAnAsyncHandler() { + Handler handler = Handler.createAsync(Looper.getMainLooper()); + + Message message = Message.obtain(); + handler.sendMessageAtTime(message, 0); + + assertTrue(message.isAsynchronous()); + } +}