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 {
class _RecordingWidgetsBinding extends BindingBase
with
GestureBinding,
ServicesBinding,
SchedulerBinding,
ServicesBinding,
PaintingBinding,
SemanticsBinding,
RendererBinding,
......
......@@ -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<LifeCycleSpy> with WidgetsBindingObserver
super.initState();
WidgetsBinding.instance.addObserver(this);
_actualLifeCycleSequence = <AppLifecycleState>[
SchedulerBinding.instance.lifecycleState
ServicesBinding.instance.lifecycleState
];
}
......
......@@ -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')}>';
}
......
......@@ -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
......
......@@ -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<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.
///
/// Defaults to [defaultSchedulingStrategy].
......
......@@ -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<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].
......
......@@ -1166,7 +1166,7 @@ class RenderObjectToWidgetElement<T extends RenderObject> 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
......
......@@ -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,
......
......@@ -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();
......
......@@ -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;
......
......@@ -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();
......
......@@ -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
......
......@@ -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();
......
......@@ -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<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{};
@override
......
......@@ -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() {
......
......@@ -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'));
});
}
......@@ -42,7 +42,7 @@ const String _extensionMethod = 'ext.flutter.$_extensionMethodName';
/// eventually completes to a string response.
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);
final DataHandler _handler;
......
......@@ -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,
......
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