-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Moved the default BinaryMessenger instance to ServicesBinding #37489
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
Conversation
|
It looks like this pull request may not have tests. Please make sure to add tests before merging. While there are exceptions to this rule, if this patch modifies code it is probably not an exception. Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
|
@Hixie @tvolkert @goderbauer |
| } | ||
|
|
||
| /// The messenger which sends the bytes for this channel, not null. | ||
| BinaryMessenger binaryMessenger; |
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.
this should be final
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.
Updated to use the _binaryMessenger getter.
| /// A default [BinaryMessenger] instance will be used if [binaryMessenger] is | ||
| /// null. | ||
| PlatformAssetBundle({this.binaryMessenger}) { | ||
| binaryMessenger ??= ServicesBinding.instance.provideBinaryMessenger(); |
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.
this can be an initializer, which allows the field to be final.
that said, see later comments for an alternative strategy that would be consistent with what we'll have to do below.
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.
| assert(name != null); | ||
| assert(codec != null); | ||
| binaryMessenger ??= ServicesBinding.instance.provideBinaryMessenger(); | ||
| } |
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 need to keep this class const.
Easiest way to do this, I think, is to have the default value of the binaryMessenger argument be null, have it set a private field _binaryMessenger, and then have BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger, where the latter is a getter that returns a precached value created during initInstances.
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.
Thanks! I'll try this approach.
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.
Updated accordingly. PTAL.
| BinaryMessenger provideBinaryMessenger() { | ||
| return defaultBinaryMessenger; | ||
| } | ||
|
|
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 think you want to flip these around. The getter should be the way to access this, to make it clear that we're not initializing a new value each time. The method should be protected, and should be called during initInstances to configure a field that defaultBinaryMessenger returns.
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.
Updated. I was wrong by treating the binaryMessenger field as private member (which is public accessible), and had this provideBinaryMessenger as the getter for it.
| final VoidCallback onCleared; | ||
|
|
||
| /// The messenger which sends the bytes for this channel, not null. | ||
| BinaryMessenger binaryMessenger; |
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 be final
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.
or just use the one from the services binding directly.
in fact here and various other places, i think it's better to do ServicesBinding.instance.defaultBinaryMessenger directly than to use a local copy. Alternatively, use a private _binaryMessenger getter that just returns ServicesBinding.instance.defaultBinaryMessenger. This avoids the local copy.
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 like the latter approach. It's generally a good practice to keep external dependencies obvious. Also, easier for injecting mocks for testing.
| /// This is used to send messages from the application to the platform, and | ||
| /// keeps track of which handlers have been registered on each channel so | ||
| /// it may dispatch incoming messages to the registered handler. | ||
| const BinaryMessenger defaultBinaryMessenger = _DefaultBinaryMessenger._(); |
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.
rather than removing this (a hard breaking change), let's just deprecate it and turn it into a getter than returns ServicesBinding.instance.defaultBinaryMessenger.
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.
Added a @deprecated annotation and updated all the tests. PTAL.
|
@Hixie This app under test reveals a case that MessageChannel could possibly be called before ServicesBinding is initialized. See the app's main. It can be fixed by switching the two lines in |
|
Generally we want people to call the binding initialization logic before they do anything anyway. |
|
(which doesn't have to be runApp, you can also do it directly) |
|
@Hixie @tvolkert @goderbauer Tests all fixed. PTAL. |
goderbauer
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
| /// keeps track of which handlers have been registered on each channel so | ||
| /// it may dispatch incoming messages to the registered handler. | ||
| const BinaryMessenger defaultBinaryMessenger = _DefaultBinaryMessenger._(); | ||
| @Deprecated('Use ServicesBinding.instance.defaultBinaryMessenger instead.') |
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.
Can you update the doc comment above as well to reflect that this is deprecated and link to the new thing that should be used?
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.
Updated. PTAL.
|
|
||
| /// Initializes a default [BinaryMessenger] instance that can be used for | ||
| /// sending platform messages. | ||
| @protected |
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.
Is there a reason this can't be private? Do we expect subclasses to override it to return a different binary messenger than the default?
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, exactly. We'll want to set a different binary messenger for the ServicesBinding used in tests.
| /// This is used to send messages from the application to the platform, and | ||
| /// keeps track of which handlers have been registered on each channel so | ||
| /// it may dispatch incoming messages to the registered handler. | ||
| BinaryMessenger defaultBinaryMessenger; |
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.
This is public and mutable. We should probably make it private with a public getter.
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.
Hmm... seems that won't work, since we'll need to set it in subclasses.
Thus, the setter has to be visible in its subclasses, otherwise we don't have a way to modify it in a subclass.
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.
Then what about changing the signature of initBinaryMessenger() to return the messenger rather than setting it? then the instance variable can be private (with a public getter), and the base class' initInstances() method can set the instance variable to the value it gets back from initBinaryMessenger().
Along with this change, maybe initBinaryMessenger() should be renamed createBinaryMessenger() or something similar?
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 was dumb... Ya, that would totally work. Updated defaultBinaryMessenger to be a private field. PTAL.
|
Included this breaking change in the Changelog. |
|
This forces plugins to choose whether to work with master or stable (sample breakage. Can we land this in phases instead? |
|
Offline comment by ada - we can call |
|
@amirh does adding |
|
Discussion offline. This requires either |
…#37489)" (#37983) This reverts commit 92ef2b9. This requires either runApp() or WidgetsFlutterBinding.ensureInitialized() to have been called before using any MethodChannels. Plugins broadly rely on MethodChannels and right now there's no general requirements that they be constructed within the runApp call, so the ecosystem breakages from this are broader than originally thought. Reverting for now.
|
For when this is re-applied: it also broke the |
|
@tvolkert Is there a way I can run these two tests before check-in? |
|
Instructions for running devicelab tests locally: https://github.com/flutter/flutter/tree/master/dev/devicelab#running-tests-locally |
Original PR: (flutter#37489), this PR reverts the revert PR (flutter#37983). This reverts commit 821602a.
Description
Moved the [default BinaryMessenger] instance (
flutter/packages/flutter/lib/src/services/binary_messenger.dart
Line 154 in 0cf4033
By this change, we are capable of registering a different default BinaryMessenger under testing environment, by creating a ServicesBinding subclass for testing. With that, we can track # of pending platform messages for synchronization purposes (see the related issue below).
Related Issues
#37409
Tests
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?
This is a breaking change: