From d47ad7ec0dc0e65aeab9b2f590908b9e4faeaa5e Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Wed, 8 Apr 2020 09:12:03 -0700 Subject: [PATCH] Reverse dependency between services and scheduler (#54212) --- .../macrobenchmarks/lib/src/web/recorder.dart | 2 +- .../flutterapp/lib/main.dart | 4 +- .../flutter/lib/src/foundation/binding.dart | 25 +++++++++- .../flutter/lib/src/rendering/binding.dart | 2 +- .../flutter/lib/src/scheduler/binding.dart | 46 ++----------------- .../flutter/lib/src/services/binding.dart | 45 ++++++++++++++++++ packages/flutter/lib/src/widgets/binding.dart | 2 +- .../foundation/service_extensions_test.dart | 4 +- .../flutter/test/painting/binding_test.dart | 9 +++- .../flutter/test/painting/painting_utils.dart | 3 +- .../test/rendering/mouse_tracking_test.dart | 2 +- .../test/rendering/rendering_tester.dart | 2 +- .../test/scheduler/animation_test.dart | 2 +- .../test/scheduler/scheduler_test.dart | 2 +- .../test/services/channel_buffers_test.dart | 3 +- .../lifecycle_test.dart | 6 +-- .../lib/src/extension/extension.dart | 2 +- packages/flutter_test/lib/src/binding.dart | 4 +- 18 files changed, 102 insertions(+), 63 deletions(-) rename packages/flutter/test/{scheduler => services}/lifecycle_test.dart (81%) diff --git a/dev/benchmarks/macrobenchmarks/lib/src/web/recorder.dart b/dev/benchmarks/macrobenchmarks/lib/src/web/recorder.dart index 12a5530e5f1..37dcdd3ea99 100644 --- a/dev/benchmarks/macrobenchmarks/lib/src/web/recorder.dart +++ b/dev/benchmarks/macrobenchmarks/lib/src/web/recorder.dart @@ -736,8 +736,8 @@ abstract class FrameRecorder { class _RecordingWidgetsBinding extends BindingBase with GestureBinding, - ServicesBinding, SchedulerBinding, + ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, diff --git a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/lib/main.dart b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/lib/main.dart index 173ba34642f..5d6b5a61db4 100644 --- a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/lib/main.dart +++ b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/lib/main.dart @@ -5,7 +5,7 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/foundation.dart'; import 'package:collection/collection.dart'; @@ -48,7 +48,7 @@ class _LifeCycleSpyState extends State with WidgetsBindingObserver super.initState(); WidgetsBinding.instance.addObserver(this); _actualLifeCycleSequence = [ - SchedulerBinding.instance.lifecycleState + ServicesBinding.instance.lifecycleState ]; } diff --git a/packages/flutter/lib/src/foundation/binding.dart b/packages/flutter/lib/src/foundation/binding.dart index 957f85906f9..126c77aad9f 100644 --- a/packages/flutter/lib/src/foundation/binding.dart +++ b/packages/flutter/lib/src/foundation/binding.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'dart:convert' show json; import 'dart:developer' as developer; import 'dart:io' show exit; -import 'dart:ui' as ui show saveCompilationTrace, Window, window; +import 'dart:ui' as ui show AppLifecycleState, saveCompilationTrace, Window, window; // Before adding any more dart:ui imports, please read the README. import 'package:meta/meta.dart'; @@ -552,6 +552,29 @@ abstract class BindingBase { }); } + // TODO(goderbauer): Remove the next two members after the service/scheduler dependencies + // have been turned around. + + /// Whether the application is visible, and if so, whether it is currently + /// interactive. + /// + /// This is set by [handleAppLifecycleStateChanged] when the + /// [SystemChannels.lifecycle] notification is dispatched. + /// + /// The preferred way to watch for changes to this value is using + /// [WidgetsBindingObserver.didChangeAppLifecycleState]. + ui.AppLifecycleState get lifecycleState => null; + + /// Called when the application lifecycle state changes. + /// + /// Notifies all the observers using + /// [WidgetsBindingObserver.didChangeAppLifecycleState]. + /// + /// This method exposes notifications from [SystemChannels.lifecycle]. + @protected + @mustCallSuper + void handleAppLifecycleStateChanged(ui.AppLifecycleState state) { } + @override String toString() => '<${objectRuntimeType(this, 'BindingBase')}>'; } diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart index 97d49a8f612..49ea361f631 100644 --- a/packages/flutter/lib/src/rendering/binding.dart +++ b/packages/flutter/lib/src/rendering/binding.dart @@ -467,7 +467,7 @@ void debugDumpSemanticsTree(DebugSemanticsDumpOrder childOrder) { /// rendering layer directly. If you are writing to a higher-level /// library, such as the Flutter Widgets library, then you would use /// that layer's binding. -class RenderingFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, SemanticsBinding, PaintingBinding, RendererBinding { +class RenderingFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, SemanticsBinding, PaintingBinding, RendererBinding { /// Creates a binding for the rendering layer. /// /// The `root` render box is attached directly to the [renderView] and is diff --git a/packages/flutter/lib/src/scheduler/binding.dart b/packages/flutter/lib/src/scheduler/binding.dart index 394c57f174e..3e64cfbfbb3 100644 --- a/packages/flutter/lib/src/scheduler/binding.dart +++ b/packages/flutter/lib/src/scheduler/binding.dart @@ -9,7 +9,6 @@ import 'dart:ui' show AppLifecycleState, FramePhase, FrameTiming, TimingsCallbac import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue; import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; import 'debug.dart'; import 'priority.dart'; @@ -197,13 +196,11 @@ enum SchedulerPhase { /// * Non-rendering tasks, to be run between frames. These are given a /// priority and are executed in priority order according to a /// [schedulingStrategy]. -mixin SchedulerBinding on BindingBase, ServicesBinding { +mixin SchedulerBinding on BindingBase { @override void initInstances() { super.initInstances(); _instance = this; - SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage); - readInitialLifecycleStateFromNativeWindow(); if (!kReleaseMode) { int frameNumber = 0; @@ -302,35 +299,19 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { /// /// The preferred way to watch for changes to this value is using /// [WidgetsBindingObserver.didChangeAppLifecycleState]. + @override AppLifecycleState get lifecycleState => _lifecycleState; AppLifecycleState _lifecycleState; - /// Initializes the [lifecycleState] with the [initialLifecycleState] from the - /// window. - /// - /// Once the [lifecycleState] is populated through any means (including this - /// method), this method will do nothing. This is because the - /// [initialLifecycleState] may already be stale and it no longer makes sense - /// to use the initial state at dart vm startup as the current state anymore. - /// - /// The latest state should be obtained by subscribing to - /// [WidgetsBindingObserver.didChangeAppLifecycleState]. - @protected - void readInitialLifecycleStateFromNativeWindow() { - if (_lifecycleState == null && _parseAppLifecycleMessage(window.initialLifecycleState) != null) { - _handleLifecycleMessage(window.initialLifecycleState); - } - } - /// Called when the application lifecycle state changes. /// /// Notifies all the observers using /// [WidgetsBindingObserver.didChangeAppLifecycleState]. /// /// This method exposes notifications from [SystemChannels.lifecycle]. - @protected - @mustCallSuper + @override void handleAppLifecycleStateChanged(AppLifecycleState state) { + super.handleAppLifecycleStateChanged(state); assert(state != null); _lifecycleState = state; switch (state) { @@ -345,25 +326,6 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { } } - Future _handleLifecycleMessage(String message) async { - handleAppLifecycleStateChanged(_parseAppLifecycleMessage(message)); - return null; - } - - static AppLifecycleState _parseAppLifecycleMessage(String message) { - switch (message) { - case 'AppLifecycleState.paused': - return AppLifecycleState.paused; - case 'AppLifecycleState.resumed': - return AppLifecycleState.resumed; - case 'AppLifecycleState.inactive': - return AppLifecycleState.inactive; - case 'AppLifecycleState.detached': - return AppLifecycleState.detached; - } - return null; - } - /// The strategy to use when deciding whether to run a task or not. /// /// Defaults to [defaultSchedulingStrategy]. diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart index 0c4ceede79f..8c8a8efeda2 100644 --- a/packages/flutter/lib/src/services/binding.dart +++ b/packages/flutter/lib/src/services/binding.dart @@ -7,6 +7,7 @@ import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; +import 'package:flutter/scheduler.dart'; import 'asset_bundle.dart'; import 'binary_messenger.dart'; @@ -27,6 +28,8 @@ mixin ServicesBinding on BindingBase { window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage; initLicenses(); SystemChannels.system.setMessageHandler(handleSystemMessage); + SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage); + readInitialLifecycleStateFromNativeWindow(); } /// The current [ServicesBinding], if one has been created. @@ -162,6 +165,48 @@ mixin ServicesBinding on BindingBase { void evict(String asset) { rootBundle.evict(asset); } + + // App life cycle + + /// Initializes the [lifecycleState] with the [initialLifecycleState] from the + /// window. + /// + /// Once the [lifecycleState] is populated through any means (including this + /// method), this method will do nothing. This is because the + /// [initialLifecycleState] may already be stale and it no longer makes sense + /// to use the initial state at dart vm startup as the current state anymore. + /// + /// The latest state should be obtained by subscribing to + /// [WidgetsBindingObserver.didChangeAppLifecycleState]. + @protected + void readInitialLifecycleStateFromNativeWindow() { + if (lifecycleState != null) { + return; + } + final AppLifecycleState state = _parseAppLifecycleMessage(window.initialLifecycleState); + if (state != null) { + handleAppLifecycleStateChanged(state); + } + } + + Future _handleLifecycleMessage(String message) async { + handleAppLifecycleStateChanged(_parseAppLifecycleMessage(message)); + return null; + } + + static AppLifecycleState _parseAppLifecycleMessage(String message) { + switch (message) { + case 'AppLifecycleState.paused': + return AppLifecycleState.paused; + case 'AppLifecycleState.resumed': + return AppLifecycleState.resumed; + case 'AppLifecycleState.inactive': + return AppLifecycleState.inactive; + case 'AppLifecycleState.detached': + return AppLifecycleState.detached; + } + return null; + } } /// The default implementation of [BinaryMessenger]. diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart index a8f01839d4b..f73504d208b 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -1166,7 +1166,7 @@ class RenderObjectToWidgetElement extends RootRenderObje /// A concrete binding for applications based on the Widgets framework. /// /// This is the glue that binds the framework to the Flutter engine. -class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { +class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { /// Returns an instance of the [WidgetsBinding], creating and /// initializing it if necessary. If one is created, it will be a diff --git a/packages/flutter/test/foundation/service_extensions_test.dart b/packages/flutter/test/foundation/service_extensions_test.dart index e0221ea3b88..e68dd6a2a86 100644 --- a/packages/flutter/test/foundation/service_extensions_test.dart +++ b/packages/flutter/test/foundation/service_extensions_test.dart @@ -16,9 +16,9 @@ import 'package:flutter/widgets.dart'; import '../flutter_test_alternative.dart'; class TestServiceExtensionsBinding extends BindingBase - with ServicesBinding, + with SchedulerBinding, + ServicesBinding, GestureBinding, - SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, diff --git a/packages/flutter/test/painting/binding_test.dart b/packages/flutter/test/painting/binding_test.dart index 74b551519be..e0b8106ade0 100644 --- a/packages/flutter/test/painting/binding_test.dart +++ b/packages/flutter/test/painting/binding_test.dart @@ -5,6 +5,7 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; @@ -85,9 +86,15 @@ class TestBindingBase implements BindingBase { @override ui.Window get window => throw UnimplementedError(); + + @override + ui.AppLifecycleState get lifecycleState => null; + + @override + void handleAppLifecycleStateChanged(ui.AppLifecycleState state) { } } -class TestPaintingBinding extends TestBindingBase with ServicesBinding, PaintingBinding { +class TestPaintingBinding extends TestBindingBase with SchedulerBinding, ServicesBinding, PaintingBinding { @override final FakeImageCache imageCache = FakeImageCache(); diff --git a/packages/flutter/test/painting/painting_utils.dart b/packages/flutter/test/painting/painting_utils.dart index 9968bad4ea1..abcb7171cc8 100644 --- a/packages/flutter/test/painting/painting_utils.dart +++ b/packages/flutter/test/painting/painting_utils.dart @@ -7,9 +7,10 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; -class PaintingBindingSpy extends BindingBase with ServicesBinding, PaintingBinding { +class PaintingBindingSpy extends BindingBase with SchedulerBinding, ServicesBinding, PaintingBinding { int counter = 0; int get instantiateImageCodecCalledCount => counter; diff --git a/packages/flutter/test/rendering/mouse_tracking_test.dart b/packages/flutter/test/rendering/mouse_tracking_test.dart index 95753d1d8dd..89d0b0581e5 100644 --- a/packages/flutter/test/rendering/mouse_tracking_test.dart +++ b/packages/flutter/test/rendering/mouse_tracking_test.dart @@ -17,7 +17,7 @@ import '../flutter_test_alternative.dart'; typedef HandleEventCallback = void Function(PointerEvent event); class _TestGestureFlutterBinding extends BindingBase - with ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, RendererBinding { + with SchedulerBinding, ServicesBinding, GestureBinding, SemanticsBinding, RendererBinding { @override void initInstances() { super.initInstances(); diff --git a/packages/flutter/test/rendering/rendering_tester.dart b/packages/flutter/test/rendering/rendering_tester.dart index dd94dfbdd28..289c1b2ce80 100644 --- a/packages/flutter/test/rendering/rendering_tester.dart +++ b/packages/flutter/test/rendering/rendering_tester.dart @@ -12,7 +12,7 @@ import 'package:flutter_test/flutter_test.dart' show EnginePhase, fail; export 'package:flutter/foundation.dart' show FlutterError, FlutterErrorDetails; export 'package:flutter_test/flutter_test.dart' show EnginePhase; -class TestRenderingFlutterBinding extends BindingBase with ServicesBinding, GestureBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding { +class TestRenderingFlutterBinding extends BindingBase with SchedulerBinding, ServicesBinding, GestureBinding, PaintingBinding, SemanticsBinding, RendererBinding { /// Creates a binding for testing rendering library functionality. /// /// If [onErrors] is not null, it is called if [FlutterError] caught any errors diff --git a/packages/flutter/test/scheduler/animation_test.dart b/packages/flutter/test/scheduler/animation_test.dart index 1518e8b09d4..441f888afdb 100644 --- a/packages/flutter/test/scheduler/animation_test.dart +++ b/packages/flutter/test/scheduler/animation_test.dart @@ -10,7 +10,7 @@ import '../flutter_test_alternative.dart'; import 'scheduler_tester.dart'; -class TestSchedulerBinding extends BindingBase with ServicesBinding, SchedulerBinding { } +class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding { } void main() { final SchedulerBinding scheduler = TestSchedulerBinding(); diff --git a/packages/flutter/test/scheduler/scheduler_test.dart b/packages/flutter/test/scheduler/scheduler_test.dart index f7a4f193ebf..729590fed32 100644 --- a/packages/flutter/test/scheduler/scheduler_test.dart +++ b/packages/flutter/test/scheduler/scheduler_test.dart @@ -12,7 +12,7 @@ import 'package:flutter/services.dart'; import '../flutter_test_alternative.dart'; import 'scheduler_tester.dart'; -class TestSchedulerBinding extends BindingBase with ServicesBinding, SchedulerBinding { +class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding { final Map>> eventsDispatched = >>{}; @override diff --git a/packages/flutter/test/services/channel_buffers_test.dart b/packages/flutter/test/services/channel_buffers_test.dart index 4d2358acde0..b0e00b288a7 100644 --- a/packages/flutter/test/services/channel_buffers_test.dart +++ b/packages/flutter/test/services/channel_buffers_test.dart @@ -9,10 +9,11 @@ import 'dart:convert'; import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -class TestChannelBuffersFlutterBinding extends BindingBase with ServicesBinding { +class TestChannelBuffersFlutterBinding extends BindingBase with SchedulerBinding, ServicesBinding { } void main() { diff --git a/packages/flutter/test/scheduler/lifecycle_test.dart b/packages/flutter/test/services/lifecycle_test.dart similarity index 81% rename from packages/flutter/test/scheduler/lifecycle_test.dart rename to packages/flutter/test/services/lifecycle_test.dart index f5d5094649d..a09b982b09c 100644 --- a/packages/flutter/test/scheduler/lifecycle_test.dart +++ b/packages/flutter/test/services/lifecycle_test.dart @@ -3,13 +3,13 @@ // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; void main() { testWidgets('initialLifecycleState is used to init state paused', (WidgetTester tester) async { // The lifecycleState is null initially in tests as there is no // initialLifecycleState. - expect(SchedulerBinding.instance.lifecycleState, equals(null)); + expect(ServicesBinding.instance.lifecycleState, equals(null)); // Mock the Window to provide paused as the AppLifecycleState final TestWidgetsFlutterBinding binding = tester.binding; // Use paused as the initial state. @@ -18,6 +18,6 @@ void main() { // The lifecycleState should now be the state we passed above, // even though no lifecycle event was fired from the platform. - expect(SchedulerBinding.instance.lifecycleState.toString(), equals('AppLifecycleState.paused')); + expect(ServicesBinding.instance.lifecycleState.toString(), equals('AppLifecycleState.paused')); }); } diff --git a/packages/flutter_driver/lib/src/extension/extension.dart b/packages/flutter_driver/lib/src/extension/extension.dart index 6fefc2e3c9a..c7bbdcf9efd 100644 --- a/packages/flutter_driver/lib/src/extension/extension.dart +++ b/packages/flutter_driver/lib/src/extension/extension.dart @@ -42,7 +42,7 @@ const String _extensionMethod = 'ext.flutter.$_extensionMethodName'; /// eventually completes to a string response. typedef DataHandler = Future Function(String message); -class _DriverBinding extends BindingBase with ServicesBinding, SchedulerBinding, GestureBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { +class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding, GestureBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { _DriverBinding(this._handler, this._silenceErrors); final DataHandler _handler; diff --git a/packages/flutter_test/lib/src/binding.dart b/packages/flutter_test/lib/src/binding.dart index 7bf83aa25d8..cfabbcb5193 100644 --- a/packages/flutter_test/lib/src/binding.dart +++ b/packages/flutter_test/lib/src/binding.dart @@ -155,8 +155,8 @@ class TestDefaultBinaryMessenger extends BinaryMessenger { /// `HttpClient` to the code making the call, so that it can appropriately mock /// or fake responses. abstract class TestWidgetsFlutterBinding extends BindingBase - with ServicesBinding, - SchedulerBinding, + with SchedulerBinding, + ServicesBinding, GestureBinding, SemanticsBinding, RendererBinding,