Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not pause rendering when android activity loses focus #4848

Merged
merged 11 commits into from Apr 12, 2018

Conversation

jonahwilliams
Copy link
Member

@jonahwilliams jonahwilliams commented Mar 22, 2018

Originally the FlutterActivity used Activity#onPause() and Activity#onResume() as a proxy for whether the Android activity was visible. This made perfect sense before splitscreen, picture in picture, and chromebooks. Currently, any time a Flutter activity loses focus, rendering will stop. This is more obvious when there is an animation playing. I haven't had time to check on a chromebook, but I imagine that losing focus there will stop rendering too.

onPause() will instead trigger the inactive state on Android, previously only used for iOS. This corresponds to foreground visible but not focused, which matches the current behavior. since the paused lifecycle is also used by iOS, this will still be used - but only onStop() for android. This is called whenever an activity goes into the background or is completely invisible.

Picture in Picture

Original on left, with fix on right.
gallery_pipgallery_working_pip

Splitscreen

Original on left, with fix on right.
gallery_splitgallery_working_split

Follow up work

Currently Flutter applications will still restart when entering splitscreen mode. The fix for this is separate, just an addition to the android:configChanges entry.

Fixes flutter/flutter#11906, flutter/flutter#14619, flutter/flutter#14665

@Hixie
Copy link
Contributor

Hixie commented Mar 22, 2018

What does this do to the app lifecycle events?
https://master-docs-flutter-io.firebaseapp.com/flutter/services/SystemChannels/lifecycle-constant.html
https://master-docs-flutter-io.firebaseapp.com/flutter/dart-ui/AppLifecycleState-class.html

The theory is that the framework is set up to be entirely in charge of when frames should be pumped; the engine should schedule frames every 16ms so long as the framework is requesting frames.

@jonahwilliams
Copy link
Member Author

We'll need to update them. I can actually move the logic to the onStop and onRestart lifecycle handlers, then we can correctly stop asking for frames.

"Interestingly" if I don't add screenLayout|density to android:configChanges, the app crashes (with the above changes, not with PR changes).

Investigating

@jonahwilliams
Copy link
Member Author

I also found the code in scheduler/binding - that should just be updated and the engine case for pausing frames adjusted. We can move the existing suspending logic into onStop

@jonahwilliams
Copy link
Member Author

I've found the cause of the assertion error (not a crash, caused by sending a message "platformChrome" when the native view doesn't exist). It seems that restarting the App, caused by the change in layout/density, caused RunBundleAndSnapshot to be re-invoked

@jonahwilliams
Copy link
Member Author

@Hixie I've updated the PR. This change doesn't require anything from the flutter side, though we should consolidate the life cycles later.

@cbracken
Copy link
Member

cbracken commented Mar 23, 2018

Generally, LGTM. /cc @jason-simmons for Android expertise.

Given that we're changing which events are fired and when, are there situations this will be a breaking change to users who rely on the current behaviour? i.e. is there any change in behaviour when not in splitscreen or using picture-in-picture mode? If pause/stop are fired very close in time in such cases, this feels fine (no need to warn flutter-dev@).

// the native view is created in a separate thread. This mostly happens when the app restarts in dev
// mode when switching into split-screen mode. Preventing app restarts on layout and density
// changes will prevent this, and afterwards this can be changed back to an assert.
if (!isAttached()) {
Copy link
Member

Choose a reason for hiding this comment

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

Does this still happen if you apply the pending engine refactoring by @chinmaygarde ? (https://github.com/chinmaygarde/flutter_engine/tree/shell)

The Dart application may fail if it sends a message to the host and the host drops the message with no response.

Copy link
Member Author

Choose a reason for hiding this comment

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

So looking into this more - I can reproduce the issue on master, somewhat flaky though. Might only be a debug issue

@@ -267,7 +267,7 @@ public void addActivityLifecycleListener(ActivityLifecycleListener listener) {
}

public void onPause() {
mFlutterLifecycleChannel.send("AppLifecycleState.paused");
mFlutterLifecycleChannel.send("AppLifecycleState.inactive");
Copy link
Member

Choose a reason for hiding this comment

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

Might be worth testing, I might be wrong, but I think on most Android manufacturer OSes, the lock screen might put us in this state too. We should be sure about battery usage for common cases.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll take a look at battery use. I'm fairly certain that Choreographer won't give use frames, so hopefully it looks okay.

Copy link
Member Author

Choose a reason for hiding this comment

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

Did some research - we might be able to tell using the PowerManager API

Copy link
Member Author

Choose a reason for hiding this comment

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

I checked a few devices (pixel 1, nexus 5, galaxy s5) - all will call onStop when exiting to the lock screen. I think this should be okay as is

@jonahwilliams
Copy link
Member Author

Still needs to be confirmed that this is unblocked by #4837 when video is working

@jonahwilliams
Copy link
Member Author

Updates:

These changes work better with the engine/shell refactor patched in. There are still some edge cases which happen if the app restarts when entering split screen. We seem to get one or two extra frames after the view has been detached, might be a race in the teardown. Also the video player plugin might have some unrelated issues on the android side.

All these issues are fixed if we add density and screenLayout to config changes. This would be a major breaking change, everyone would have to update their androidManifest, though they should anyway so their apps don't restart.

Thoughts @Hixie , @jason-simmons ?

@Hixie
Copy link
Contributor

Hixie commented Apr 9, 2018

I'm fine with adding things to the android manifest. We should just make sure to document it in the changelog. Ideally we'd use @xster's upgrade idea to autoupgrade people's projects, but that can be a stretch goal.

@Hixie
Copy link
Contributor

Hixie commented Apr 9, 2018

LGTM

@Hixie
Copy link
Contributor

Hixie commented Apr 10, 2018

Looks like we already updated the manifest in flutter/flutter#15871, in fact.

@jonahwilliams
Copy link
Member Author

I've confirmed that this works on the latest flutter version locally, including video

@jonahwilliams jonahwilliams merged commit c83d1ef into flutter:master Apr 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
6 participants