Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 233a2e0

Browse files
[Windows] Delay enabling app lifecycle states until requested (#44238)
Await a platform message before sending lifecycle state updates so we are not sending messages that do not get consumed by the framework. In the _near_ future we hope to extend the embedder API to allow registering callbacks called upon the framework registering a listener to a channel, which would obviate this problem. flutter/flutter#131616 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 18f72b5 commit 233a2e0

File tree

5 files changed

+157
-9
lines changed

5 files changed

+157
-9
lines changed

shell/platform/windows/fixtures/main.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,38 @@ void exitTestCancel() async {
127127
await exited.future;
128128
}
129129

130+
@pragma('vm:entry-point')
131+
void enableLifecycleTest() async {
132+
final Completer<ByteData?> finished = Completer<ByteData?>();
133+
ui.channelBuffers.setListener('flutter/lifecycle', (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
134+
if (data != null) {
135+
ui.PlatformDispatcher.instance.sendPlatformMessage(
136+
'flutter/unittest',
137+
data,
138+
(ByteData? reply) {
139+
finished.complete();
140+
});
141+
}
142+
});
143+
await finished.future;
144+
}
145+
146+
@pragma('vm:entry-point')
147+
void enableLifecycleToFrom() async {
148+
ui.channelBuffers.setListener('flutter/lifecycle', (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
149+
if (data != null) {
150+
ui.PlatformDispatcher.instance.sendPlatformMessage(
151+
'flutter/unittest',
152+
data,
153+
(ByteData? reply) {});
154+
}
155+
});
156+
final Completer<ByteData?> enabledLifecycle = Completer<ByteData?>();
157+
ui.PlatformDispatcher.instance.sendPlatformMessage('flutter/platform', ByteData.sublistView(utf8.encode('{"method":"System.initializationComplete"}')), (ByteData? data) {
158+
enabledLifecycle.complete(data);
159+
});
160+
}
161+
130162
@pragma('vm:entry-point')
131163
void customEntrypoint() {}
132164

shell/platform/windows/flutter_windows_engine.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,8 +792,14 @@ void FlutterWindowsEngine::OnDwmCompositionChanged() {
792792
view_->OnDwmCompositionChanged();
793793
}
794794

795+
// TODO(yaakovschectman): This enables the flutter/lifecycle channel
796+
// once the System.initializationComplete message is received on
797+
// the flutter/system channel. This is a short-term workaround to
798+
// ensure the framework is initialized and ready to accept lifecycle
799+
// messages. This cross-channel dependency should be removed.
800+
// See: https://github.com/flutter/flutter/issues/132514
795801
void FlutterWindowsEngine::OnApplicationLifecycleEnabled() {
796-
lifecycle_manager_->BeginProcessingClose();
802+
lifecycle_manager_->BeginProcessingLifecycle();
797803
}
798804

799805
void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,

shell/platform/windows/flutter_windows_engine_unittests.cc

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,15 @@ class MockWindowsLifecycleManager : public WindowsLifecycleManager {
670670
MOCK_METHOD4(DispatchMessage, void(HWND, UINT, WPARAM, LPARAM));
671671
MOCK_METHOD0(IsLastWindowOfProcess, bool(void));
672672
MOCK_METHOD1(SetLifecycleState, void(AppLifecycleState));
673+
674+
void BeginProcessingLifecycle() override {
675+
WindowsLifecycleManager::BeginProcessingLifecycle();
676+
if (begin_processing_callback) {
677+
begin_processing_callback();
678+
}
679+
}
680+
681+
std::function<void()> begin_processing_callback = nullptr;
673682
};
674683

675684
TEST_F(FlutterWindowsEngineTest, TestExit) {
@@ -1002,5 +1011,105 @@ TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) {
10021011
AppLifecycleState::kInactive);
10031012
}
10041013

1014+
TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
1015+
FlutterWindowsEngineBuilder builder{GetContext()};
1016+
builder.SetDartEntrypoint("enableLifecycleTest");
1017+
bool finished = false;
1018+
1019+
auto window_binding_handler =
1020+
std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1021+
MockFlutterWindowsView view(std::move(window_binding_handler));
1022+
view.SetEngine(builder.Build());
1023+
FlutterWindowsEngine* engine = view.GetEngine();
1024+
1025+
EngineModifier modifier(engine);
1026+
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1027+
auto handler = std::make_unique<MockWindowsLifecycleManager>(engine);
1028+
ON_CALL(*handler, SetLifecycleState)
1029+
.WillByDefault([handler_ptr = handler.get()](AppLifecycleState state) {
1030+
handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1031+
});
1032+
modifier.SetLifecycleManager(std::move(handler));
1033+
1034+
auto binary_messenger =
1035+
std::make_unique<BinaryMessengerImpl>(engine->messenger());
1036+
// Mark the test only as completed on receiving an inactive state message.
1037+
binary_messenger->SetMessageHandler(
1038+
"flutter/unittest", [&finished](const uint8_t* message,
1039+
size_t message_size, BinaryReply reply) {
1040+
std::string contents(message, message + message_size);
1041+
EXPECT_NE(contents.find("AppLifecycleState.inactive"),
1042+
std::string::npos);
1043+
finished = true;
1044+
char response[] = "";
1045+
reply(reinterpret_cast<uint8_t*>(response), 0);
1046+
});
1047+
1048+
engine->Run();
1049+
1050+
// Test that setting the state before enabling lifecycle does nothing.
1051+
HWND hwnd = reinterpret_cast<HWND>(1);
1052+
view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1053+
view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1054+
EXPECT_FALSE(finished);
1055+
1056+
// Test that we can set the state afterwards.
1057+
engine->OnApplicationLifecycleEnabled();
1058+
view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1059+
1060+
while (!finished) {
1061+
engine->task_runner()->ProcessTasks();
1062+
}
1063+
}
1064+
1065+
TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
1066+
FlutterWindowsEngineBuilder builder{GetContext()};
1067+
builder.SetDartEntrypoint("enableLifecycleToFrom");
1068+
bool enabled_lifecycle = false;
1069+
bool dart_responded = false;
1070+
1071+
auto window_binding_handler =
1072+
std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1073+
MockFlutterWindowsView view(std::move(window_binding_handler));
1074+
view.SetEngine(builder.Build());
1075+
FlutterWindowsEngine* engine = view.GetEngine();
1076+
1077+
EngineModifier modifier(engine);
1078+
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1079+
auto handler = std::make_unique<MockWindowsLifecycleManager>(engine);
1080+
ON_CALL(*handler, SetLifecycleState)
1081+
.WillByDefault([handler_ptr = handler.get()](AppLifecycleState state) {
1082+
handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1083+
});
1084+
handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
1085+
modifier.SetLifecycleManager(std::move(handler));
1086+
1087+
auto binary_messenger =
1088+
std::make_unique<BinaryMessengerImpl>(engine->messenger());
1089+
binary_messenger->SetMessageHandler(
1090+
"flutter/unittest",
1091+
[&](const uint8_t* message, size_t message_size, BinaryReply reply) {
1092+
std::string contents(message, message + message_size);
1093+
EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
1094+
dart_responded = true;
1095+
char response[] = "";
1096+
reply(reinterpret_cast<uint8_t*>(response), 0);
1097+
});
1098+
1099+
engine->Run();
1100+
1101+
while (!enabled_lifecycle) {
1102+
engine->task_runner()->ProcessTasks();
1103+
}
1104+
1105+
HWND hwnd = reinterpret_cast<HWND>(1);
1106+
view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1107+
view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1108+
1109+
while (!dart_responded) {
1110+
engine->task_runner()->ProcessTasks();
1111+
}
1112+
}
1113+
10051114
} // namespace testing
10061115
} // namespace flutter

shell/platform/windows/windows_lifecycle_manager.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
namespace flutter {
1515

1616
WindowsLifecycleManager::WindowsLifecycleManager(FlutterWindowsEngine* engine)
17-
: engine_(engine), process_close_(false) {}
17+
: engine_(engine) {}
1818

1919
WindowsLifecycleManager::~WindowsLifecycleManager() {}
2020

@@ -49,7 +49,7 @@ bool WindowsLifecycleManager::WindowProc(HWND hwnd,
4949
// is, we re-dispatch a new WM_CLOSE message. In order to allow the new
5050
// message to reach other delegates, we ignore it here.
5151
case WM_CLOSE: {
52-
if (!process_close_) {
52+
if (!process_lifecycle_) {
5353
return false;
5454
}
5555
auto key = std::make_tuple(hwnd, wpar, lpar);
@@ -179,8 +179,8 @@ bool WindowsLifecycleManager::IsLastWindowOfProcess() {
179179
return num_windows <= 1;
180180
}
181181

182-
void WindowsLifecycleManager::BeginProcessingClose() {
183-
process_close_ = true;
182+
void WindowsLifecycleManager::BeginProcessingLifecycle() {
183+
process_lifecycle_ = true;
184184
}
185185

186186
// TODO(schectman): Wait until the platform channel is registered to send
@@ -191,7 +191,7 @@ void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) {
191191
return;
192192
}
193193
state_ = state;
194-
if (engine_) {
194+
if (engine_ && process_lifecycle_) {
195195
const char* state_name = AppLifecycleStateToString(state);
196196
engine_->SendPlatformMessage("flutter/lifecycle",
197197
reinterpret_cast<const uint8_t*>(state_name),

shell/platform/windows/windows_lifecycle_manager.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ class WindowsLifecycleManager {
5252
// update the application lifecycle.
5353
bool WindowProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l, LRESULT* result);
5454

55-
// Signal to start consuming WM_CLOSE messages.
56-
void BeginProcessingClose();
55+
// Signal to start consuming WM_CLOSE messages and sending lifecycle state
56+
// update messages.
57+
virtual void BeginProcessingLifecycle();
5758

5859
// Update the app lifecycle state in response to a change in window state.
5960
// When the app lifecycle state actually changes, this sends a platform
@@ -100,7 +101,7 @@ class WindowsLifecycleManager {
100101

101102
std::map<std::tuple<HWND, WPARAM, LPARAM>, int> sent_close_messages_;
102103

103-
bool process_close_;
104+
bool process_lifecycle_ = false;
104105

105106
std::set<HWND> visible_windows_;
106107

0 commit comments

Comments
 (0)