-
Notifications
You must be signed in to change notification settings - Fork 6k
Automatically register plugins in FlutterEngine. (#43855) #13455
Automatically register plugins in FlutterEngine. (#43855) #13455
Conversation
| * {@code flutterJNI} should be a new instance that has never been attached to an engine before. | ||
| * Same as {@link #FlutterEngine(Context, FlutterLoader, FlutterJNI)}, plus Dart VM flags in | ||
| * {@code dartVmArgs}, and control over whether plugins are automatically registered with this | ||
| * {@code FlutterEngine} in {@code automaticallyRegisterPlugins}. |
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.
say when they're registered
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.
Done.
| this | ||
| ); | ||
|
|
||
| if (automaticallyRegisterPlugins) { |
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 haven't fully thought this out yet to have an opinion either way but do we want this on construction or when the dart executor runs?
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.
Exactly what I came here to ask. :-)
I would do it here because of the background use case. Plugins should be registered as quickly as possible when the process starts. With the new API, they will get callbacks when activity etc is available. This is what my SingleIsolate design dictates.
However...
I also acknowledge the need to have capability to register plugins at a later point when activity is available. There are quite a few plugins using old API that assume registrar.activity() is not null when registerWith() is called.
So, the current (internal) solution supports both. We expose a registerBackgroundPlugins method where a background registrant is called and we also allow the usual registrant to be called on Activity.onCreate() for plugins that make that assumption.
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 a strong opinion either way. I suppose if we wait until dart execution begins then at least the BinaryMessenger would be ready. I'm not sure if registering plugins in the constructor will create any issues for existing plugins or not.
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's probably safer to do it after dart execution begins. I don't think we have a comprehensive solution for queue'ing messages sent before the BinaryMessenger is ready, so that might drop some messages.
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.
Looking into the code, we don't have a callback when Dart execution begins. I could add a callback when DartExecutor is told to start executing Dart code, but the asynchronous boundary will still cause the callback to run before the Dart side is truly setup.
What do you think?
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.
Indeed.
I'm not sure if registering plugins in the constructor will create any issues for existing plugins or not.
It will for the ones that are expecting an registrar.activity() not to be null in their registerWith function. Those can only be created later. I have been relentlessly cleaning these up wherever I see them. It is quite easy since almost no plugin needs an activity during registration. They either save it for later or save a service they derive from it for later.
I don't think we have a comprehensive solution for queue'ing messages sent before the BinaryMessenger is ready, so that might drop some messages.
I decided to put the onus on the plugins on this. If the plugin generates messages without getting any signal from Dart, likely that is a plugin that's doing work in the background (since it is responding to some non-Dart event). Each such plugin need to cache their messages until they get a signal that Dart is ready...
Looking into the code, we don't have a callback when Dart execution begins. I could add a callback when DartExecutor is told to start executing Dart code, but the asynchronous boundary will still cause the callback to run before the Dart side is truly setup.
Dart side is not truly setup until the plugin's callback has registered. That is, application code needs to register the plugin and then call some sort of function (like registerCallbacks) which will indicate that the Dart endpoint is listening.
See: go/writing-flutter-plugins and scroll to the bottom for my recommendations of background plugins.
Can we do better?
May be? We could allow for "cold" platform channels where instead of dropping messages on Dart side, we save them in a queue attached to the platform channel name. That way, the plugins do not have to worry about initialization. We then deliver the messages to that channel when it starts listening. This is too much magic for me but there might be a design in there somewhere that might work.
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.
So it sounds like this location for initialization is reasonable given existing limitations. Is that right?
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.
In the scope of this PR, yes.
Sorry for the long post. Hopefully, it was effective in getting across some of the design decisions I made while thinking about background executions.
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.
Those details are good to know. I figured background plugins could have problems with Activity, but I didn't realize that foreground uses might have issues, too. But it sounds like those problems exist no matter what we do here.
|
LGTM |
| */ | ||
| private void registerPlugins() { | ||
| try { | ||
| Class generatedPluginRegistrant = Class.forName("dev.plugins.GeneratedPluginRegistrant"); |
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.
fyi, I filed flutter/flutter#43869
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 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 particular preference. I supposed it's not a terribly grave decision. Any opinions @mit-mit in terms of what namespace we put the new generated registration?
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.
yep. I don't see value in changing the namespace.
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 spoke offline. We're gonna go back to "io" because none of the migrated plugins ended up using "dev".
| @RunWith(RobolectricTestRunner.class) | ||
| public class FlutterEngineTest { | ||
| @Test | ||
| public void itDoesNotCrashIfGeneratedPluginRegistrantIsUnavailable() { |
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.
Should we also have a test when the registrant is available and verify that registerWith is called?
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.
Would that be a test in the engine or framework?
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.
Ideally, both. If you add one here, you will get an extra signal earlier.
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.
Spoke offline. Testing infra appears to be too limited to test both the positive and negative case, so we'll leave as-is.
Automatically register plugins in FlutterEngine. (#43855)
Proposal here:
https://docs.google.com/document/d/1xNkBmcdVL1yEXqtZ65KzTwfr5UXDD05VVKYXIXGX7p8/edit?usp=sharing