mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Reland "Avoid vsync scheduling delay" (flutter/engine#36197)
This commit is contained in:
parent
2ae612c5df
commit
abbff8407e
@ -1648,6 +1648,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
|
||||
|
||||
@ -289,6 +289,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",
|
||||
|
||||
@ -7,10 +7,13 @@ package io.flutter.embedding.engine.dart;
|
||||
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) {
|
||||
|
||||
@ -23,6 +23,7 @@ import io.flutter.BuildConfig;
|
||||
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 class FlutterLoader {
|
||||
Log.e(TAG, "Flutter initialization failed.", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
new Handler(Looper.getMainLooper())
|
||||
HandlerCompat.createAsyncHandler(Looper.getMainLooper())
|
||||
.post(
|
||||
() -> {
|
||||
ensureInitializationComplete(applicationContext.getApplicationContext(), args);
|
||||
|
||||
@ -224,19 +224,29 @@ public class PlatformPlugin {
|
||||
new View.OnSystemUiVisibilityChangeListener() {
|
||||
@Override
|
||||
public void onSystemUiVisibilityChange(int visibility) {
|
||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
||||
// The system bars are visible. Make any desired adjustments to
|
||||
// your UI, such as showing the action bar or other navigational
|
||||
// controls. Another common action is to set a timer to dismiss
|
||||
// the system bars and restore the fullscreen mode that was
|
||||
// previously enabled.
|
||||
platformChannel.systemChromeChanged(false);
|
||||
} else {
|
||||
// The system bars are NOT visible. Make any desired adjustments
|
||||
// to your UI, such as hiding the action bar or other
|
||||
// navigational controls.
|
||||
platformChannel.systemChromeChanged(true);
|
||||
}
|
||||
// `platformChannel.systemChromeChanged` may trigger a callback that eventually results
|
||||
// in a call to `setSystemUiVisibility`.
|
||||
// `setSystemUiVisibility` must not be called in the same frame as when
|
||||
// `onSystemUiVisibilityChange` is received though.
|
||||
//
|
||||
// As such, post `platformChannel.systemChromeChanged` to the view handler to ensure
|
||||
// that downstream callbacks are trigged on the next frame.
|
||||
decorView.post(
|
||||
() -> {
|
||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
||||
// The system bars are visible. Make any desired adjustments to
|
||||
// your UI, such as showing the action bar or other navigational
|
||||
// controls. Another common action is to set a timer to dismiss
|
||||
// the system bars and restore the fullscreen mode that was
|
||||
// previously enabled.
|
||||
platformChannel.systemChromeChanged(false);
|
||||
} else {
|
||||
// The system bars are NOT visible. Make any desired adjustments
|
||||
// to your UI, such as hiding the action bar or other
|
||||
// navigational controls.
|
||||
platformChannel.systemChromeChanged(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*
|
||||
* <p>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:
|
||||
*
|
||||
* <ul>
|
||||
* <li>SDK 28 and above, this method matches platform behavior.
|
||||
* <li>Otherwise, returns a synchronous handler instance.
|
||||
* </ul>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.anyBoolean;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@ -27,7 +28,6 @@ import android.view.WindowManager;
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel.Brightness;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel.ClipboardContentFormat;
|
||||
@ -38,10 +38,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.android.controller.ActivityController;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
|
||||
@Config(manifest = Config.NONE)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class PlatformPluginTest {
|
||||
private final Context ctx = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@ -368,6 +371,32 @@ public class PlatformPluginTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSystemUiModeListener() {
|
||||
ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class);
|
||||
controller.setup();
|
||||
Activity fakeActivity = controller.get();
|
||||
|
||||
PlatformChannel fakePlatformChannel = mock(PlatformChannel.class);
|
||||
PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel);
|
||||
|
||||
// Subscribe to system Ui visibility events.
|
||||
platformPlugin.mPlatformMessageHandler.setSystemUiChangeListener();
|
||||
|
||||
// Simulate changing of the system ui visibility.
|
||||
fakeActivity.getWindow().getDecorView().dispatchSystemUiVisibilityChanged(0);
|
||||
|
||||
// No events should have been sent to the platform channel yet. They are scheduled for
|
||||
// the next frame.
|
||||
verify(fakePlatformChannel, never()).systemChromeChanged(anyBoolean());
|
||||
|
||||
// Simulate the next frame.
|
||||
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
|
||||
|
||||
// Now the platform channel should receive the event.
|
||||
verify(fakePlatformChannel).systemChromeChanged(anyBoolean());
|
||||
}
|
||||
|
||||
@Config(sdk = 28)
|
||||
@Test
|
||||
public void doNotEnableEdgeToEdgeOnOlderSdk() {
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,7 @@
|
||||
<src file="../../../flutter/shell/platform/android/io/flutter/util/Predicate.java" />
|
||||
<src file="../../../flutter/shell/platform/android/io/flutter/util/TraceSection.java" />
|
||||
<src file="../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java" />
|
||||
<src file="../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java" />
|
||||
<src file="../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyboardMap.java" />
|
||||
<src file="../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyboardManager.java" />
|
||||
<src file="../../../flutter/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user