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

Federated Plugins? #473

Closed
keaganhilliard opened this issue Sep 23, 2020 · 22 comments
Closed

Federated Plugins? #473

keaganhilliard opened this issue Sep 23, 2020 · 22 comments
Assignees
Labels
2 fixing enhancement New feature or request

Comments

@keaganhilliard
Copy link
Contributor

Is your feature request related to a problem? Please describe.
I have been reading about federated plugins and I think we can make the implementation of different platforms for audio service cleaner using a more federated model. Currently, we have a lot of unnecessary overhead with PlatformChannels on the web. I think we can make this better.

Describe the solution you'd like
I want to adopt a model more akin to what's described here by the flutter team. This would involve creating a Platform Interface and then extending that for our different implementations. I think this would make implementation of new platforms cleaner.

Describe alternatives you've considered
We already have a working alternative.

Additional context
None.

@hacker1024
Copy link
Contributor

I like this idea - it would also make developing for new platforms easier, and unofficial implementations (or even implementations under a different license) possible.

@ryanheise
Copy link
Owner

I certainly agree there are benefits which have been discussed recently on the just_audio issues page, although there is more of an immediate case to do this for just_audio, and I'd like to delay the move for audio_service a bit longer. A few reasons below:

  1. just_audio implementations may depend on decoders that have restrictive licenses and we NEED that flexibility. By contrast, audio_service uses only the native APIs of the operating system or platform which don't have restrictive licenses and don't require that flexibility.
  2. just_audio's API is now relatively stable. audio_service has some upcoming changes (primarily to the API) that may slow down development if done in the wrong order.

Before switching to the federated plugin model, I would like to adopt pigeon, and before I adopt pigeon, I would like to complete the API redesign. At the same time, I wouldn't want to block development of additional platform support, so I think we can still continue under the current model while that work is being done, and then we can probably transition to federated plugins at the earliest opportunity once those prior steps are done.

@ryanheise
Copy link
Owner

I just evaluated pigeon and IMO it's not ready for prime time yet, so I'll probably skip that part of the plan.

@keaganhilliard
Copy link
Contributor Author

Gotcha it definitely makes sense. I was thinking that I wanted to get rid of the binary serialization on the web if I could because it's unnecessary. Seemed like the federated plugins model would work well, but I totally agree it should be done after we have a more stable API.

Interesting that pigeon isn't quite ready, seems like they are using it for the official video player plugin. What's missing with it?

@ryanheise
Copy link
Owner

Pigeon doesn't yet support asynchronous method calls or event channels. It's a promising technology, but probably not a good idea to adopt it this early.

I was thinking that I wanted to get rid of the binary serialization on the web if I could because it's unnecessary. Seemed like the federated plugins model would work well, but I totally agree it should be done after we have a more stable API.

It would be possible to use the same technique that federated plugins use to solve this problem, without actually creating a federated plugin. That is, by creating a platform interface which the web implementation extends, and then also having a method channel implementation of the same interface. But, I think we can live with the current implementation for now, and we can address this during the transition to federated plugins.

@keaganhilliard
Copy link
Contributor Author

keaganhilliard commented Sep 29, 2020

Honestly I think you are right, we don't really need the whole federated plugin thing for this, but a platform interface would be super helpful which is mostly what I was driving at and what made me think it would be a good idea. I'm in the initial stages of starting on Windows support and I think ffi might be the way to go for some of the media APIs so it would be possible to do everything without a platform channel on windows as well. Definitely needs more discovery though (not sure how playing in the background works, flutter for windows has a strange tendency to restart the app after you minimize/maximize).

EDIT: Actually it looks like people smarter than me are wrapping the MediaPlayer apis on windows. So I might just wait for them to finish (halildurmus/win32#100) 😄

@ryanheise
Copy link
Owner

ryanheise commented Sep 30, 2020

Over the weekend I converted just_audio into a federated plugin, and my experience from it was that the federated part was actually quite easy to set up, while designing the platform interface itself was the difficult part. Granted, the reason why it took a lot of care to design the platform interface is because federated plugins allow independently developed implementations to be plugged in, any future change to the platform interface could break those independently developed implementations, and so the platform interface must be designed for future compatibility. So in theory I could create a platform interface for audio_service that's quick and dirty on the understanding that it will very likely change in the future.

I still would like to delay the creation of a platform interface for as long as possible since the main thing I'm preoccupied with now is a redesign of the API and this is something I'd like to avoid doing twice. But if you decide that the FFI is the way to go, that's probably a good reason for me to create a platform interface.

@keaganhilliard
Copy link
Contributor Author

I probably won't get to it for a bit so there's no rush and even if I do I can just work through it in a similar fashion to the web support. It's definitely more important to have a well designed API that's as future proof as possible. Thanks for all of the hard work!

@ryanheise
Copy link
Owner

I have just committed to the one-isolate branch a migration to the federated plugin architecture, with Android and iOS implementations done. I've also updated the migration guide with instructions on how to pin to a specific commit since there will probably be some temporary breakages, but hopefully if people report any problems, we can get them fixed quickly.

While designing the platform interface, I realised that some of the messages in AudioClientCallbacks could be implemented purely in Dart through inter-isolate communication rather than going through the platform, since the source of these events come from your AudioHandler which is running in Dart code in an isolate. But where these callbacks might be needed is if in the future we allow connecting to a remote Android media session (in another process).

We still have a chance to make some refinements to the platform interface up until the point of release.

@nt4f04uNd
Copy link
Contributor

nt4f04uNd commented Apr 5, 2021

is there any particular reason you do not extend/implement messages, but rather do _toMessage? is that to not expose toMap to the plugin's classes?

@nt4f04uNd
Copy link
Contributor

nt4f04uNd commented Apr 5, 2021

also, why do toMap methods return Map<dynamic, dynamic>, not Map<String, dynamic>?
upd: submitted a #640 that fixes this

@ryanheise
Copy link
Owner

This is because Map<dynamic, dynamic> is the raw type of maps sent over the platform channel. You can get runtime errors if you assume that you can typecast the data to Map<String, dynamic> and so whenever you need the keys to be strings, you need to call the cast() method. The only time when I use Map<String, dynamic> is if it is for a user-facing data structure where I want strong typing, but when sending data over the platform channels, I use the raw encoding for efficiency.

@ryanheise
Copy link
Owner

also, why do toMap methods return Map<dynamic, dynamic>, not Map<String, dynamic>?

This is because Map<dynamic, dynamic> is the raw type of maps sent over the platform channel. You can get runtime errors if you assume that you can typecast the data to Map<String, dynamic> and so whenever you need the keys to be strings, you need to call the cast() method. The only time when I use `Map<String, dynamic

@ryanheise
Copy link
Owner

is there any particular reason you do not extend/implement messages, but rather do _toMessage? is that to not expose toMap to the plugin's classes?

Just basic architecture, and not letting things bleed through layers. I'm not necessarily strict on this point except that I definitely do not want internals bleeding out into the public plugin API. Composition over inheritance. The platform interface is a completely separate layer intended only for communicating with the platform, that is it's purpose and though it is tempting to see the overlap and try to inherit, it also makes interactions with the platform interface more fragile. There are some discussions in various Google documents about the thinking behind the federated plugin architecture, and there is also ongoing work with Pigeon which will probably help you make sense of the role of messages (I'd like to use Pigeon someday but in my opinion it's not ready for prime time yet).

@nt4f04uNd
Copy link
Contributor

The only time when I use Map<String, dynamic> is if it is for a user-facing data structure where I want strong typing, but when sending data over the platform channels, I use the raw encoding for efficiency.

remember the user of an interface is developer, so think about them too

most of the issues with cast go from as, and there's also a convenience method invokeMapMethod that helps address that (alongside with mentioned cast)

@nt4f04uNd
Copy link
Contributor

nt4f04uNd commented Jun 10, 2021

WDYT of converting Map<String, dynamic> to Map<String, Object?>. This is kinda nit, but allows users of the interface (including us) not do casts like map['value'] as String, which will crash in runtime, if value happens to be null

@nt4f04uNd
Copy link
Contributor

nt4f04uNd commented Jun 10, 2021

It likely will be better if we replace all dynamics with Object?, including the custom state. Yes, this will make people do casts and typechecks, but at least it will reveal errors statically, rather than swallow them until the code executes

@ryanheise
Copy link
Owner

While I'm all for static analysis, I'm inclined to leave it as Map<String, dynamic> since it appears to be a standard across Dart and that will help if I ever want to use a code generation library that's hard coded to use this map type.

@nt4f04uNd
Copy link
Contributor

You are right.

There are a few places where cast goes wrong in the interface, I will fix them along with adding unit tests for the interface, and submit a PR. You were only working on tests for plugin, not the interface yet, right? (Just so we don't do the same thing concurrently)

@ryanheise
Copy link
Owner

I won't be touching the tests until the weekend so feel free to work in this area 👍 .

@nt4f04uNd
Copy link
Contributor

nt4f04uNd commented Jun 11, 2021

Why is the return value of CustomAction is Future<dynamic>? It's void on native side

UPD: ok I think for the same reason you described in #415 (comment)

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
2 fixing enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants