Unverified Commit d47ad7ec authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Reverse dependency between services and scheduler (#54212)

parent 090fc5cc
...@@ -736,8 +736,8 @@ abstract class FrameRecorder { ...@@ -736,8 +736,8 @@ abstract class FrameRecorder {
class _RecordingWidgetsBinding extends BindingBase class _RecordingWidgetsBinding extends BindingBase
with with
GestureBinding, GestureBinding,
ServicesBinding,
SchedulerBinding, SchedulerBinding,
ServicesBinding,
PaintingBinding, PaintingBinding,
SemanticsBinding, SemanticsBinding,
RendererBinding, RendererBinding,
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
...@@ -48,7 +48,7 @@ class _LifeCycleSpyState extends State<LifeCycleSpy> with WidgetsBindingObserver ...@@ -48,7 +48,7 @@ class _LifeCycleSpyState extends State<LifeCycleSpy> with WidgetsBindingObserver
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
_actualLifeCycleSequence = <AppLifecycleState>[ _actualLifeCycleSequence = <AppLifecycleState>[
SchedulerBinding.instance.lifecycleState ServicesBinding.instance.lifecycleState
]; ];
} }
......
...@@ -6,7 +6,7 @@ import 'dart:async'; ...@@ -6,7 +6,7 @@ import 'dart:async';
import 'dart:convert' show json; import 'dart:convert' show json;
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'dart:io' show exit; 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. // Before adding any more dart:ui imports, please read the README.
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -552,6 +552,29 @@ abstract class BindingBase { ...@@ -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 @override
String toString() => '<${objectRuntimeType(this, 'BindingBase')}>'; String toString() => '<${objectRuntimeType(this, 'BindingBase')}>';
} }
......
...@@ -467,7 +467,7 @@ void debugDumpSemanticsTree(DebugSemanticsDumpOrder childOrder) { ...@@ -467,7 +467,7 @@ void debugDumpSemanticsTree(DebugSemanticsDumpOrder childOrder) {
/// rendering layer directly. If you are writing to a higher-level /// rendering layer directly. If you are writing to a higher-level
/// library, such as the Flutter Widgets library, then you would use /// library, such as the Flutter Widgets library, then you would use
/// that layer's binding. /// 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. /// Creates a binding for the rendering layer.
/// ///
/// The `root` render box is attached directly to the [renderView] and is /// The `root` render box is attached directly to the [renderView] and is
......
...@@ -9,7 +9,6 @@ import 'dart:ui' show AppLifecycleState, FramePhase, FrameTiming, TimingsCallbac ...@@ -9,7 +9,6 @@ import 'dart:ui' show AppLifecycleState, FramePhase, FrameTiming, TimingsCallbac
import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue; import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'debug.dart'; import 'debug.dart';
import 'priority.dart'; import 'priority.dart';
...@@ -197,13 +196,11 @@ enum SchedulerPhase { ...@@ -197,13 +196,11 @@ enum SchedulerPhase {
/// * Non-rendering tasks, to be run between frames. These are given a /// * Non-rendering tasks, to be run between frames. These are given a
/// priority and are executed in priority order according to a /// priority and are executed in priority order according to a
/// [schedulingStrategy]. /// [schedulingStrategy].
mixin SchedulerBinding on BindingBase, ServicesBinding { mixin SchedulerBinding on BindingBase {
@override @override
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
_instance = this; _instance = this;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
readInitialLifecycleStateFromNativeWindow();
if (!kReleaseMode) { if (!kReleaseMode) {
int frameNumber = 0; int frameNumber = 0;
...@@ -302,35 +299,19 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -302,35 +299,19 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
/// ///
/// The preferred way to watch for changes to this value is using /// The preferred way to watch for changes to this value is using
/// [WidgetsBindingObserver.didChangeAppLifecycleState]. /// [WidgetsBindingObserver.didChangeAppLifecycleState].
@override
AppLifecycleState get lifecycleState => _lifecycleState; AppLifecycleState get lifecycleState => _lifecycleState;
AppLifecycleState _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. /// Called when the application lifecycle state changes.
/// ///
/// Notifies all the observers using /// Notifies all the observers using
/// [WidgetsBindingObserver.didChangeAppLifecycleState]. /// [WidgetsBindingObserver.didChangeAppLifecycleState].
/// ///
/// This method exposes notifications from [SystemChannels.lifecycle]. /// This method exposes notifications from [SystemChannels.lifecycle].
@protected @override
@mustCallSuper
void handleAppLifecycleStateChanged(AppLifecycleState state) { void handleAppLifecycleStateChanged(AppLifecycleState state) {
super.handleAppLifecycleStateChanged(state);
assert(state != null); assert(state != null);
_lifecycleState = state; _lifecycleState = state;
switch (state) { switch (state) {
...@@ -345,25 +326,6 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -345,25 +326,6 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
} }
} }
Future<String> _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. /// The strategy to use when deciding whether to run a task or not.
/// ///
/// Defaults to [defaultSchedulingStrategy]. /// Defaults to [defaultSchedulingStrategy].
......
...@@ -7,6 +7,7 @@ import 'dart:typed_data'; ...@@ -7,6 +7,7 @@ import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'asset_bundle.dart'; import 'asset_bundle.dart';
import 'binary_messenger.dart'; import 'binary_messenger.dart';
...@@ -27,6 +28,8 @@ mixin ServicesBinding on BindingBase { ...@@ -27,6 +28,8 @@ mixin ServicesBinding on BindingBase {
window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage; window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
initLicenses(); initLicenses();
SystemChannels.system.setMessageHandler(handleSystemMessage); SystemChannels.system.setMessageHandler(handleSystemMessage);
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
readInitialLifecycleStateFromNativeWindow();
} }
/// The current [ServicesBinding], if one has been created. /// The current [ServicesBinding], if one has been created.
...@@ -162,6 +165,48 @@ mixin ServicesBinding on BindingBase { ...@@ -162,6 +165,48 @@ mixin ServicesBinding on BindingBase {
void evict(String asset) { void evict(String asset) {
rootBundle.evict(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<String> _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]. /// The default implementation of [BinaryMessenger].
......
...@@ -1166,7 +1166,7 @@ class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObje ...@@ -1166,7 +1166,7 @@ class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObje
/// A concrete binding for applications based on the Widgets framework. /// A concrete binding for applications based on the Widgets framework.
/// ///
/// This is the glue that binds the framework to the Flutter engine. /// 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 /// Returns an instance of the [WidgetsBinding], creating and
/// initializing it if necessary. If one is created, it will be a /// initializing it if necessary. If one is created, it will be a
......
...@@ -16,9 +16,9 @@ import 'package:flutter/widgets.dart'; ...@@ -16,9 +16,9 @@ import 'package:flutter/widgets.dart';
import '../flutter_test_alternative.dart'; import '../flutter_test_alternative.dart';
class TestServiceExtensionsBinding extends BindingBase class TestServiceExtensionsBinding extends BindingBase
with ServicesBinding, with SchedulerBinding,
ServicesBinding,
GestureBinding, GestureBinding,
SchedulerBinding,
PaintingBinding, PaintingBinding,
SemanticsBinding, SemanticsBinding,
RendererBinding, RendererBinding,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -85,9 +86,15 @@ class TestBindingBase implements BindingBase { ...@@ -85,9 +86,15 @@ class TestBindingBase implements BindingBase {
@override @override
ui.Window get window => throw UnimplementedError(); 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 @override
final FakeImageCache imageCache = FakeImageCache(); final FakeImageCache imageCache = FakeImageCache();
......
...@@ -7,9 +7,10 @@ import 'dart:ui' as ui; ...@@ -7,9 +7,10 @@ import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.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 counter = 0;
int get instantiateImageCodecCalledCount => counter; int get instantiateImageCodecCalledCount => counter;
......
...@@ -17,7 +17,7 @@ import '../flutter_test_alternative.dart'; ...@@ -17,7 +17,7 @@ import '../flutter_test_alternative.dart';
typedef HandleEventCallback = void Function(PointerEvent event); typedef HandleEventCallback = void Function(PointerEvent event);
class _TestGestureFlutterBinding extends BindingBase class _TestGestureFlutterBinding extends BindingBase
with ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, RendererBinding { with SchedulerBinding, ServicesBinding, GestureBinding, SemanticsBinding, RendererBinding {
@override @override
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
......
...@@ -12,7 +12,7 @@ import 'package:flutter_test/flutter_test.dart' show EnginePhase, fail; ...@@ -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/foundation.dart' show FlutterError, FlutterErrorDetails;
export 'package:flutter_test/flutter_test.dart' show EnginePhase; 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. /// Creates a binding for testing rendering library functionality.
/// ///
/// If [onErrors] is not null, it is called if [FlutterError] caught any errors /// If [onErrors] is not null, it is called if [FlutterError] caught any errors
......
...@@ -10,7 +10,7 @@ import '../flutter_test_alternative.dart'; ...@@ -10,7 +10,7 @@ import '../flutter_test_alternative.dart';
import 'scheduler_tester.dart'; import 'scheduler_tester.dart';
class TestSchedulerBinding extends BindingBase with ServicesBinding, SchedulerBinding { } class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding { }
void main() { void main() {
final SchedulerBinding scheduler = TestSchedulerBinding(); final SchedulerBinding scheduler = TestSchedulerBinding();
......
...@@ -12,7 +12,7 @@ import 'package:flutter/services.dart'; ...@@ -12,7 +12,7 @@ import 'package:flutter/services.dart';
import '../flutter_test_alternative.dart'; import '../flutter_test_alternative.dart';
import 'scheduler_tester.dart'; import 'scheduler_tester.dart';
class TestSchedulerBinding extends BindingBase with ServicesBinding, SchedulerBinding { class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding {
final Map<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{}; final Map<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{};
@override @override
......
...@@ -9,10 +9,11 @@ import 'dart:convert'; ...@@ -9,10 +9,11 @@ import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
class TestChannelBuffersFlutterBinding extends BindingBase with ServicesBinding { class TestChannelBuffersFlutterBinding extends BindingBase with SchedulerBinding, ServicesBinding {
} }
void main() { void main() {
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart';
void main() { void main() {
testWidgets('initialLifecycleState is used to init state paused', (WidgetTester tester) async { testWidgets('initialLifecycleState is used to init state paused', (WidgetTester tester) async {
// The lifecycleState is null initially in tests as there is no // The lifecycleState is null initially in tests as there is no
// initialLifecycleState. // initialLifecycleState.
expect(SchedulerBinding.instance.lifecycleState, equals(null)); expect(ServicesBinding.instance.lifecycleState, equals(null));
// Mock the Window to provide paused as the AppLifecycleState // Mock the Window to provide paused as the AppLifecycleState
final TestWidgetsFlutterBinding binding = tester.binding; final TestWidgetsFlutterBinding binding = tester.binding;
// Use paused as the initial state. // Use paused as the initial state.
...@@ -18,6 +18,6 @@ void main() { ...@@ -18,6 +18,6 @@ void main() {
// The lifecycleState should now be the state we passed above, // The lifecycleState should now be the state we passed above,
// even though no lifecycle event was fired from the platform. // 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'));
}); });
} }
...@@ -42,7 +42,7 @@ const String _extensionMethod = 'ext.flutter.$_extensionMethodName'; ...@@ -42,7 +42,7 @@ const String _extensionMethod = 'ext.flutter.$_extensionMethodName';
/// eventually completes to a string response. /// eventually completes to a string response.
typedef DataHandler = Future<String> Function(String message); typedef DataHandler = Future<String> 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); _DriverBinding(this._handler, this._silenceErrors);
final DataHandler _handler; final DataHandler _handler;
......
...@@ -155,8 +155,8 @@ class TestDefaultBinaryMessenger extends BinaryMessenger { ...@@ -155,8 +155,8 @@ class TestDefaultBinaryMessenger extends BinaryMessenger {
/// `HttpClient` to the code making the call, so that it can appropriately mock /// `HttpClient` to the code making the call, so that it can appropriately mock
/// or fake responses. /// or fake responses.
abstract class TestWidgetsFlutterBinding extends BindingBase abstract class TestWidgetsFlutterBinding extends BindingBase
with ServicesBinding, with SchedulerBinding,
SchedulerBinding, ServicesBinding,
GestureBinding, GestureBinding,
SemanticsBinding, SemanticsBinding,
RendererBinding, RendererBinding,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment