-
Notifications
You must be signed in to change notification settings - Fork 6k
Enforce exclusivity for activity and fragments attached to the FlutterEngine #21272
Conversation
284be07 to
436ecd6
Compare
| super.onDestroy(); | ||
| delegate.onDestroyView(); | ||
| delegate.onDetach(); | ||
| Log.e("meh", "Activity " + this + " onDestroy"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debugging log.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, thanks!
| Log.v(TAG, "FlutterActivity " + this + " onDestroy called after release."); | ||
| } | ||
| lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might need to check the delegate nullness in lifecycle methods below as well (onActivityResult and such). At the very least, let's confirm with an Android expert (perhaps clm) that those methods are never called after onPause().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya, sensible. Ping'ed him on the bug.
| "FlutterActivityAndFragmentDelegate's getAppComponent should only " | ||
| + "be queried after onAttach, when the host's activity should always be non-null"); | ||
| } | ||
| return host.getActivity(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just return activity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you just mean don't do assertions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I mean you are doing final activity = host.getActivity() above. Why not return the local var?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah thanks
| // -------- Start ActivityControlSurface ------- | ||
| private boolean isAttachedToActivity() { | ||
| return activity != null; | ||
| return activity != null || exclusiveActivity != null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between activity and exclusiveActivity? As far as plugins are concerned, isn't there always one activity? It would be dangerous to have different plugins be attached to different activity instances.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is only one. This is just extra fluff since I didn't want to make the deprecation too hard breaking.
| // If we were already attached to an Android component, detach from it. | ||
| detachFromAndroidComponent(); | ||
| if (this.exclusiveActivity != null) { | ||
| this.exclusiveActivity.detachFromFlutterEngine(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is odd that activity/engine relationship is being managed in plugin registry..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya, that's the last piece I noted in the PR summary above. I think the place to put it is right (e.g. consolidating the place where we attach/detach, (e.g. if a person makes a custom activity, there should be burden to attach it to the engine in just one API instead of 3 or 4)).
The name is not right. The easiest solution is to just name this FlutterEngineConnectionRegistry since this file here is sibling here with the engine rather than in the plugins package. For super maintenance, we can also split this into 2 files to make it shorter than 800 LOC. But I don't think we need to do that right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've renamed the package private class FlutterEngineConnectionRegistry to reflect its role.
| * avoid situations where multiple activities are driving the FlutterEngine simultaneously. | ||
| * See https://github.com/flutter/flutter/issues/66192. | ||
| */ | ||
| @Deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am surprised this is public API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have any proof that anyone's using it but it is public API. The theoretical usage I think would be fore someone who make a custom activity that holds a FlutterView. This public interface is the APIs that lets that FlutterView's owner tell that FlutterView's FlutterEngine's plugins when an activity is available / unavailable.
That's why there's that activity/exclusiveActivity thing above. It's just transitional so we have a not-too-rough breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair.
So, there will be an announcement and a deprecation phase so we can clean it up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, writing website PR at the moment.
| protected void onNewIntent(@NonNull Intent intent) { | ||
| // TODO(mattcarroll): change G3 lint rule that forces us to call super | ||
| super.onNewIntent(intent); | ||
| ensureAlive(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is needed since these methods would not normally be called by user code. They are entirely controlled by Android lifecycle itself and Android guarantees not calling these methods once activity is destroyed.
ensureAlive type thing made some sense in delegate because delegate could be used in many objects (including fragment and activity) it had to assert its own internal state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense. Thanks
mehmetf
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Fixes flutter/flutter#66192.
The fix is pretty simple. It just lets the activity and fragment detach themselves when another activity or fragment is attaching to the engine. There's some added ceremony since it's such a central piece that I wanted to make it a bit harder to drift accidentally in the future.
Alternatives considered:
Deprecation doc flutter/website#4640