mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Issues/39832 reland (#13642)
* Reland "Added new lifecycle enum (#11913)"
This commit is contained in:
parent
76312eefdd
commit
1bfb928e07
@ -171,18 +171,16 @@ enum AppLifecycleState {
|
||||
///
|
||||
/// When the application is in this state, the engine will not call the
|
||||
/// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks.
|
||||
///
|
||||
/// Android apps in this state should assume that they may enter the
|
||||
/// [suspending] state at any time.
|
||||
paused,
|
||||
|
||||
/// The application will be suspended momentarily.
|
||||
/// The application is still hosted on a flutter engine but is detached from
|
||||
/// any host views.
|
||||
///
|
||||
/// When the application is in this state, the engine will not call the
|
||||
/// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks.
|
||||
///
|
||||
/// On iOS, this state is currently unused.
|
||||
suspending,
|
||||
/// When the application is in this state, the engine is running without
|
||||
/// a view. It can either be in the progress of attaching a view when engine
|
||||
/// was first initializes, or after the view being destroyed due to a Navigator
|
||||
/// pop.
|
||||
detached,
|
||||
}
|
||||
|
||||
/// A representation of distances for each of the four edges of a rectangle,
|
||||
|
||||
@ -73,18 +73,13 @@ enum AppLifecycleState {
|
||||
///
|
||||
/// When the application is in this state, the engine will not call the
|
||||
/// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks.
|
||||
///
|
||||
/// Android apps in this state should assume that they may enter the
|
||||
/// [suspending] state at any time.
|
||||
paused,
|
||||
|
||||
/// The application will be suspended momentarily.
|
||||
/// The application is detached from view.
|
||||
///
|
||||
/// When the application is in this state, the engine will not call the
|
||||
/// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks.
|
||||
///
|
||||
/// On iOS, this state is currently unused.
|
||||
suspending,
|
||||
/// When the application is in this state, the engine is running without
|
||||
/// a platform UI.
|
||||
detached,
|
||||
}
|
||||
|
||||
/// A representation of distances for each of the four edges of a rectangle,
|
||||
|
||||
@ -120,7 +120,7 @@ class RuntimeController final : public WindowClient {
|
||||
std::string variant_code;
|
||||
std::vector<std::string> locale_data;
|
||||
std::string user_settings_data = "{}";
|
||||
std::string lifecycle_state;
|
||||
std::string lifecycle_state = "AppLifecycleState.detached";
|
||||
bool semantics_enabled = false;
|
||||
bool assistive_technology_enabled = false;
|
||||
int32_t accessibility_feature_flags_ = 0;
|
||||
|
||||
@ -311,7 +311,7 @@ bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) {
|
||||
const auto& data = message->data();
|
||||
std::string state(reinterpret_cast<const char*>(data.data()), data.size());
|
||||
if (state == "AppLifecycleState.paused" ||
|
||||
state == "AppLifecycleState.suspending") {
|
||||
state == "AppLifecycleState.detached") {
|
||||
activity_running_ = false;
|
||||
StopAnimator();
|
||||
} else if (state == "AppLifecycleState.resumed" ||
|
||||
|
||||
@ -461,6 +461,8 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
|
||||
platformPlugin = null;
|
||||
}
|
||||
|
||||
flutterEngine.getLifecycleChannel().appIsDetached();
|
||||
|
||||
// Destroy our FlutterEngine if we're not set to retain it.
|
||||
if (host.shouldDestroyEngineWithHost()) {
|
||||
flutterEngine.destroy();
|
||||
|
||||
@ -39,4 +39,8 @@ public class LifecycleChannel {
|
||||
channel.send("AppLifecycleState.paused");
|
||||
}
|
||||
|
||||
public void appIsDetached() {
|
||||
Log.v(TAG, "Sending AppLifecycleState.detached message.");
|
||||
channel.send("AppLifecycleState.detached");
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,18 +85,21 @@ public class FlutterActivityAndFragmentDelegateTest {
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached();
|
||||
|
||||
// When the Activity/Fragment is resumed, a resumed message should have been sent to Flutter.
|
||||
delegate.onResume();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached();
|
||||
|
||||
// When the Activity/Fragment is paused, an inactive message should have been sent to Flutter.
|
||||
delegate.onPause();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached();
|
||||
|
||||
// When the Activity/Fragment is stopped, a paused message should have been sent to Flutter.
|
||||
// Notice that Flutter uses the term "paused" in a different way, and at a different time
|
||||
@ -105,6 +108,14 @@ public class FlutterActivityAndFragmentDelegateTest {
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached();
|
||||
|
||||
// When activity detaches, a detached message should have been sent to Flutter.
|
||||
delegate.onDetach();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsResumed();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsInactive();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsPaused();
|
||||
verify(mockFlutterEngine.getLifecycleChannel(), times(1)).appIsDetached();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -175,18 +175,23 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
|
||||
- (void)setViewController:(FlutterViewController*)viewController {
|
||||
FML_DCHECK(self.iosPlatformView);
|
||||
_viewController = [viewController getWeakPtr];
|
||||
_viewController =
|
||||
viewController ? [viewController getWeakPtr] : fml::WeakPtr<FlutterViewController>();
|
||||
self.iosPlatformView->SetOwnerViewController(_viewController);
|
||||
[self maybeSetupPlatformViewChannels];
|
||||
|
||||
__block FlutterEngine* blockSelf = self;
|
||||
self.flutterViewControllerWillDeallocObserver =
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
|
||||
object:viewController
|
||||
queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification* note) {
|
||||
[blockSelf notifyViewControllerDeallocated];
|
||||
}];
|
||||
if (viewController) {
|
||||
__block FlutterEngine* blockSelf = self;
|
||||
self.flutterViewControllerWillDeallocObserver =
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
|
||||
object:viewController
|
||||
queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification* note) {
|
||||
[blockSelf notifyViewControllerDeallocated];
|
||||
}];
|
||||
} else {
|
||||
self.flutterViewControllerWillDeallocObserver = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
|
||||
@ -201,6 +206,7 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
}
|
||||
|
||||
- (void)notifyViewControllerDeallocated {
|
||||
[[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
|
||||
if (!_allowHeadlessExecution) {
|
||||
[self destroyContext];
|
||||
} else {
|
||||
|
||||
@ -225,4 +225,70 @@
|
||||
[engine setViewController:nil];
|
||||
}
|
||||
|
||||
- (void)testFlutterViewControllerDetachingSendsApplicationLifecycle {
|
||||
XCTestExpectation* engineStartedExpectation = [self expectationWithDescription:@"Engine started"];
|
||||
|
||||
// Let the engine finish booting (at the end of which the channels are properly set-up) before
|
||||
// moving onto the next step of showing the next view controller.
|
||||
ScreenBeforeFlutter* rootVC = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:^void() {
|
||||
[engineStartedExpectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectationsWithTimeout:5 handler:nil];
|
||||
|
||||
UIApplication* application = UIApplication.sharedApplication;
|
||||
application.delegate.window.rootViewController = rootVC;
|
||||
FlutterEngine* engine = rootVC.engine;
|
||||
|
||||
NSMutableArray* lifecycleExpectations = [NSMutableArray arrayWithCapacity:10];
|
||||
|
||||
// Expected sequence from showing the FlutterViewController is inactive and resumed.
|
||||
[lifecycleExpectations addObjectsFromArray:@[
|
||||
[[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive"
|
||||
forStep:@"showing a FlutterViewController"],
|
||||
[[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed"
|
||||
forStep:@"showing a FlutterViewController"]
|
||||
]];
|
||||
// At the end of Flutter VC, we want to make sure it deallocs and sends detached signal.
|
||||
// Using autoreleasepool will guarantee that.
|
||||
FlutterViewController* flutterVC;
|
||||
@autoreleasepool {
|
||||
flutterVC = [rootVC showFlutter];
|
||||
[engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) {
|
||||
if (lifecycleExpectations.count == 0) {
|
||||
XCTFail(@"Unexpected lifecycle transition: %@", message);
|
||||
return;
|
||||
}
|
||||
XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0];
|
||||
if (![[nextExpectation expectedLifecycle] isEqualToString:message]) {
|
||||
XCTFail(@"Expected lifecycle %@ but instead received %@",
|
||||
[nextExpectation expectedLifecycle], message);
|
||||
return;
|
||||
}
|
||||
|
||||
[nextExpectation fulfill];
|
||||
[lifecycleExpectations removeObjectAtIndex:0];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:lifecycleExpectations timeout:5];
|
||||
|
||||
// Starts dealloc flutter VC.
|
||||
[lifecycleExpectations addObjectsFromArray:@[
|
||||
[[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.inactive"
|
||||
forStep:@"detaching a FlutterViewController"],
|
||||
[[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.paused"
|
||||
forStep:@"detaching a FlutterViewController"],
|
||||
[[XCAppLifecycleTestExpectation alloc]
|
||||
initForLifecycle:@"AppLifecycleState.detached"
|
||||
forStep:@"detaching a FlutterViewController"]
|
||||
]];
|
||||
[flutterVC dismissViewControllerAnimated:NO completion:nil];
|
||||
flutterVC = nil;
|
||||
}
|
||||
[self waitForExpectations:lifecycleExpectations timeout:5];
|
||||
|
||||
[engine.lifecycleChannel setMessageHandler:nil];
|
||||
[engine setViewController:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user