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

Conversation

@chunhtai
Copy link
Contributor

@chunhtai chunhtai commented Sep 5, 2019

This is one of the step toward fixing add to app warm up issue
design doc

This pr adds a new AppLifecycleState.detached, and make sure flutter embedder sends out the correct signal when engine is initialized.

This pr also includes removes AppLifecycleState.suspend, It seems it is not longer used

This will be a breaking change and require manual engine roll.
framework pr https://github.com/flutter/flutter/pull/39945/files

flutter/flutter#39832

Here is the breaking change email draft

@dnfield
Copy link
Contributor

dnfield commented Sep 6, 2019

@chunhtai - I'm using your PR to test building on LUCI infra presubmit. Failures may not indicate a real failure and might be safe to ignore (although the Cirrus failure shoul dbe addressed).

@chunhtai
Copy link
Contributor Author

chunhtai commented Sep 6, 2019

@chunhtai - I'm using your PR to test building on LUCI infra presubmit. Failures may not indicate a real failure and might be safe to ignore (although the Cirrus failure shoul dbe addressed).

ok thanks for letting me know, the cirrus test is expected to fail, this require framework change.

@dnfield
Copy link
Contributor

dnfield commented Sep 6, 2019

All worked as expected - woohoo!

@chunhtai chunhtai requested review from goderbauer and xster September 6, 2019 18:42
/// The application is detached from view.
///
/// On iOS, this state is currently unused.
suspending,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cbracken
It doesn't seem like we are still using this enum any where in our code. Since this pr will be a breaking change anyway. Is it ok to remove this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason not to deprecate for one stable release , document as unused, then delete it in order to make the transition slightly less painful to users?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is less painful to remove the enum and add the new enum in one pr. If you think it is better to add a document warning ahead of time, I can do a document only change pr first and wait for some release cycle before merge this one

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm for doing it in one PR as well. Adding an enum value is already a non-gracefully-failable breaking change. We already can't make a soft transition.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enum is still used in the framework. It looks like flutter/flutter#39945 will remove it (from scheduler/binding.dart), but that PR hasn't been merged yet. As a result, this PR will block engine rolls moving forward.

}

- (void)notifyViewControllerDeallocated {
[[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how to test this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xster has been working on some tests related to these and can probably help

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think #11890 might make some part of your test a bit easier. You'd have to change add a bit to push another VC after the Flutter VC and change the assert value after you pop the Flutter VC.

I expect to have that one reworked (retranslate to obj-c) by monday

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated existing test

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm tempted to suggest that we keep the logic that does all this stuff in the same place. i.e. all the rest of the lifecycle system channel transitions are sourced in flutterviewcontroller (as is the source of the signal that triggers this call). We might as well keep them all in the same place.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

umm that's a good point. I think that's also generally true for all other references to the engine from the VC in the first place.

Maybe there isn't any immediate, great solutions. I'm tempted to say, since there's already ~40 "incorrect" _engine.get() in the flutter vc, we might as well keep this one in the same place (for easier refactor in the future since they're all together) and leave a bug for us to clean this up together when the engine sets a flutter view controller that's not the current one.

WDYT @dnfield, @gaaclarke?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this specific one, this seems like the right move. If I understand correctly, this is specifically to let us know about a detatched state of the VC - the VC can't necessarily tell us that because it might try to tell us too late (at a point where it's reference to the engine is lost).

It would be worth auditing other places where we try to send information via the engine, but as long as it's information that only a makes sense in an attached state it should be fine to keep it in the VC. If not, we should move it to the engine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For instance, there are a few lifecycle messages we send based on whether the view is coming or going about the Application state - those probably should get refactored so they work more sensibly in add-to-app contexts if that's possible.

The other usages of _engine.get() all look like they are correct to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will leave this as is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tangential to this PR now:

as long as it's information that only a makes sense in an attached state it should be fine to keep it in the VC

the point @chunhtai was pointing to originally I think is that if the VC did get detached from the engine, the VC would never know and would crash at some point. It's not happening right now because removing the engine from a currently active foreground vc is not something people do commonly. But we should audit.

Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure to also have this reviewed by somebody on the engine team.

/// 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 in the progress of creating a view, or the view
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can either be...

///
/// On iOS, this state is currently unused.
suspending,
/// When the application is in this state, the engine is running without
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs more documentation to understand what this state actually means. Can this state happen in the non add2app case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can happens whenever the condition is met during engine life cycle, and yes it also happens in non add2app case. I found it weird to specifically mention add2app/non add2app in the comment, so I didn't include it. I rephrased a little bit to make it more descriptive.

@chunhtai
Copy link
Contributor Author

ready for re-review

Copy link
Member

@xster xster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

///
/// When the application is in this state, the engine will not call the
/// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks.
/// The application is still hosted on flutter engine but is detached from
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is still hosted on a Flutter engine

/// When the application is in this state, the engine will not call the
/// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks.
/// The application is still hosted on flutter engine but is detached from
/// view.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any host views.

/// 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 creating a view when engine
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

progress of attaching to a view when the engine was first initialized

(because Flutter main not be the one responsible for creating any views)

suspending,
/// When the application is in this state, the engine is running without
/// a view. It can either be in the progress of creating a view when engine
/// first initializes, or the view has been destroyed due to a Navigator pop.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or after the view being destroyed.

(I wouldn't necessarily reference the SystemNavigator API since the user can initiate a pop from the platform side too. A pop also pops the view but might not necessarily destroy the view)

this,
androidLifecycle
);
lifecycleChannel.appIsDetached();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you hedging on the fact that Aaron is supporting channel buffers for this (since no entrypoint/binding is running by this point).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this will be end up stored in

window._initialLifecycleState = state;

if the binding hasn't initialized yet?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah you're right, we had an existing single featured buffer here :)

verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsResumed();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsPaused();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsInactive();
verify(mockFlutterEngine.getLifecycleChannel(), never()).appIsDetached();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like it's getting a bit verbose. Can we just verifyNoMoreInteractions? (it may not work though if there's more things going on in the mock but I suspect not)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is detached will be called at the end. I think we cannot use verifyNoMoreInteractions?

initWithDescription:@"A second FlutterViewController goes "
@"through AppLifecycleState.inactive"]];
[lifecycleExpectations
addObject:[[XCTestExpectation alloc]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect that this test will fail earlier once @gaaclarke's channel buffer comes through right? Right now it's not crashing because of an extraneous detached signal at the beginning because the detached message you're sending on the FlutterEngine construct is just being dropped on the floor because no one is listening by that point on the framework right?

initWithDescription:
@"A second FlutterViewController goes through AppLifecycleState.resumed"]];
flutterVC = [rootVC showFlutter];
// At the end of second Flutter VC, we want to make sure it deallocs and sends detached signal.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of making this one longer, can you make a different test?

self.iosPlatformView->SetOwnerViewController(_viewController);
[self maybeSetupPlatformViewChannels];

[_flutterViewControllerWillDeallocObserver release];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xster I am not sure if this the appropriate fix.
I ran into an issue that calling setViewController multiple time will register multiple observer for flutter vc. This happens in one of my test where i do [engine setViewController:nil] at the end which causes notifyViewControllerDeallocated to be called twice.

I think it make more sense to release the previous one in the case of setting multiple times

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gaaclarke, I have a feeling we had this discussion but didn't do it for some reason. You might have a better recollection of the reasoning.

@cbracken
Copy link
Member

@chunhtai any plans to land this in the near future or should it be closed out?

@xster
Copy link
Member

xster commented Oct 25, 2019

I think he's out this week but we should try to merge this in. I'll review this again (I'm back)

@chunhtai
Copy link
Contributor Author

@chunhtai any plans to land this in the near future or should it be closed out?

There were some refactorings to android embedding, i will have to modify the code a little bit.

I will get this merged in these few days

@chunhtai
Copy link
Contributor Author

@xster this should be ready to go. Can you take another look?

/// 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 in the progress of creating a view, or the view
Copy link
Member

@xster xster Oct 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the process of creating a view sounds a bit vague and/or overly inclusive. One might assume this would happen with every view creation process of any normal full-flutter apps, which isn't the case. Just say that this happens when the Flutter tree is populated without a platform UI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on the dart code the the flutter tree might or might not exist. I will just mention the engine is running without a platform UI

<< entrypoint.UTF8String;
} else {
[self setupChannels];
[[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit strange that we're imperatively triggering an edge-triggered signal to set a level-triggered signal. Just letting window._initialLifecycleState = detached seems semantically more correct but also a bit riskier.

std::vector<std::string> locale_data;
std::string user_settings_data = "{}";
std::string lifecycle_state;
std::string lifecycle_state = "AppLifecycleState.detached";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xster This will be true as long as dart project is run after engine is initialized

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no direct api for engine to modify window.dart property. Even if we drill one down, it still have to go through shell, and that will be the same case as before.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lgtm. Curious why here and not in window.dart's _initialLifecycleState

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the window hook initialized, runtime controller will send these datas to window hook to initialize it. So the data here means to be the default value of the window hook

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Thanks

Copy link
Member

@xster xster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

std::vector<std::string> locale_data;
std::string user_settings_data = "{}";
std::string lifecycle_state;
std::string lifecycle_state = "AppLifecycleState.detached";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lgtm. Curious why here and not in window.dart's _initialLifecycleState

[engine setViewController:nil];
}

- (void)testDetachedFlutterViewControllerRespondsToApplicationLifecycle {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is not quite right. I think you meant something like testFlutterViewControllerDetachingSendsApplicationLifecycle or something.

}

- (void)notifyViewControllerDeallocated {
[[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

umm that's a good point. I think that's also generally true for all other references to the engine from the VC in the first place.

Maybe there isn't any immediate, great solutions. I'm tempted to say, since there's already ~40 "incorrect" _engine.get() in the flutter vc, we might as well keep this one in the same place (for easier refactor in the future since they're all together) and leave a bug for us to clean this up together when the engine sets a flutter view controller that's not the current one.

WDYT @dnfield, @gaaclarke?

@xster
Copy link
Member

xster commented Oct 31, 2019

LGTM

@chunhtai chunhtai added the waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. label Nov 4, 2019
@cbracken
Copy link
Member

cbracken commented Nov 4, 2019

From build_and_test_linux_unopt_debug:

   +1 ms] Exception: ../../../packages/flutter/lib/src/scheduler/binding.dart:336:30:
           Error: Getter not found: 'suspending'.
                 case AppLifecycleState.suspending:
                                        ^^^^^^^^^^
           ../../../packages/flutter/lib/src/scheduler/binding.dart:356:34:
           Error: Getter not found: 'suspending'.
                   return AppLifecycleState.suspending;
                                            ^^^^^^^^^^

@xster
Copy link
Member

xster commented Nov 4, 2019

Thanks for checking Chris. I guess this one we'd have to fix with a different PR on the framework @chunhtai

@chunhtai
Copy link
Contributor Author

chunhtai commented Nov 4, 2019

From build_and_test_linux_unopt_debug:

   +1 ms] Exception: ../../../packages/flutter/lib/src/scheduler/binding.dart:336:30:
           Error: Getter not found: 'suspending'.
                 case AppLifecycleState.suspending:
                                        ^^^^^^^^^^
           ../../../packages/flutter/lib/src/scheduler/binding.dart:356:34:
           Error: Getter not found: 'suspending'.
                   return AppLifecycleState.suspending;
                                            ^^^^^^^^^^

This is expected, it require an manual roll. I am waiting for luci to be green

@chunhtai chunhtai merged commit 02a4790 into flutter:master Nov 4, 2019
chunhtai added a commit to chunhtai/engine that referenced this pull request Nov 4, 2019
chunhtai added a commit that referenced this pull request Nov 4, 2019
chunhtai added a commit to chunhtai/engine that referenced this pull request Nov 4, 2019
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 5, 2019
engine-flutter-autoroll added a commit to flutter/flutter that referenced this pull request Nov 5, 2019
[email protected]:flutter/engine.git/compare/e73c9c8f6bd5...7b968ff

git log e73c9c8..7b968ff --no-merges --oneline
2019-11-05 [email protected] Ensure that the CAMetalLayer FBO attachments can be read from. (flutter/engine#13643)
2019-11-04 [email protected] Revert "Added new lifecycle enum (#11913)" (flutter/engine#13632)
2019-11-04 [email protected] Added new lifecycle enum (flutter/engine#11913)


If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC [email protected] on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
chunhtai added a commit that referenced this pull request Nov 5, 2019
* Reland "Added new lifecycle enum (#11913)"
Inconnu08 pushed a commit to Inconnu08/flutter that referenced this pull request Nov 26, 2019
[email protected]:flutter/engine.git/compare/e73c9c8f6bd5...7b968ff

git log e73c9c8..7b968ff --no-merges --oneline
2019-11-05 [email protected] Ensure that the CAMetalLayer FBO attachments can be read from. (flutter/engine#13643)
2019-11-04 [email protected] Revert "Added new lifecycle enum (flutter#11913)" (flutter/engine#13632)
2019-11-04 [email protected] Added new lifecycle enum (flutter/engine#11913)


If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC [email protected] on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
yunsion added a commit to yunsion/flutter_lifecycle_state that referenced this pull request Nov 10, 2020
uerceg pushed a commit to adjust/flutter_sdk that referenced this pull request Oct 7, 2022
As reported on [Jira](https://adjustcom.atlassian.net/browse/ES-3620), the `AppLifecycleState.suspending` method is no longer supported by Flutter. This has been replaced by `AppLifecycleState.detached`.

[Flutter PR for context](flutter/engine#11913)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

cla: yes prod: API break waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants