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

Unable to call a platform channel method from a another isolate #13937

Open
sroddy opened this Issue Jan 5, 2018 · 12 comments

Comments

Projects
None yet
@sroddy
Copy link
Contributor

sroddy commented Jan 5, 2018

When I try to invoke a platform channel method from a custom spawned isolate, the app crashes badly (both on iOS and Android). I'm trying to figure out whether this is expected or not.

If it's not, it's probably worth to mention that somewhere.

Anyways I think that this can potentially be a strong limitation. Is there a way to be able to call platform plugins from a secondary isolate?

@Hixie

This comment has been minimized.

Copy link
Contributor

Hixie commented Jan 8, 2018

cc @mravn-google for triage

@mravn-google

This comment has been minimized.

Copy link
Contributor

mravn-google commented Jan 11, 2018

The engine code dereferences null and crashes when attempting to send a platform message from the secondary isolate. I haven't pinpointed exactly where yet; I get a tombstone, but need to learn how to interpret such a thing.

As a (clumsy) workaround, the secondary isolate could ask the primary one to send the message.

@sroddy Might I ask what you are trying to accomplish with the secondary isolate?

@sroddy

This comment has been minimized.

Copy link
Contributor Author

sroddy commented Jan 12, 2018

I have various use cases, e.g.:

  • Download and parse a huge chunk of data (dart:convert methods are not async) , then store it to a sqlite db using sqflite plugin (which uses platform bindings);
  • Prefetch and parse data before serving it to the ui;
  • Encrypt/decrypt and read/write data from/to a file (crypto stuff is done through a method channel in order to use platform security libs);

In general it can happen that you use dependencies that declare some public methods which you don't really know if eventually will use platform-specific code to accomplish their stuff (I'm thinking about flutterfire for example); using them imho should be more an implementation detail that can change with time instead of something written in stone.

Your workaround seems ok for the moment and relatively painless to implement, as data is already encoded in a way to be passed to method channel, and for this reason will pass easily also via an isolate port, however my guess is that performances would be suboptimal.

However I got a bit stuck during implementation: can you provide an example of an easy way to invoke a static method on the main isolate from a secondary one?

Thanks

@mravn-google

This comment has been minimized.

Copy link
Contributor

mravn-google commented Jan 12, 2018

@sroddy Thanks for the providing the background information.

About invoking static methods on the main isolate: it is not directly supported and has to be implemented using ports. This package may provide that functionality, e.g. here. Haven't tried it myself though.

But we should look for a more general solution here, one that works also when platform channel communication is done as part of the implementation of a plugin or other library.

We don't quite have the hooks for this yet, but if we can assume for a moment that you have a known set of channel names that are to be used from the secondary isolate and not the main one, you should be able to generically reconfigure platform message handling yourself for those channels, transparently implementing the necessary forwarding of binary messages and replies between your two isolates. Plugins would never know the difference.

Solution sketched below.

Suppose you set up ports M1 and R1 on your main isolate and M2, R2 on your secondary isolate. M for message, R for reply.

  • In your secondary isolate, use BinaryMessages.setMockMessageHandler for each channel to forward messages for the platform to M1 (transparently to the plugin that uses BinaryMessage.send with that channel). Store reply callbacks and configure R2 with a handler that invokes the correct one on receipt of the reply for the message. Configure M2 with a handler that forwards to BinaryMessages.handlePlatformMessage. Implement the reply callback there to forward replies to R1.
  • Symmetrically in your main isolate. Configure M1 with a handler that forwards messages for the platform to BinaryMessages.send. Set a reply callback that forwards replies from the platform to R2. Also call BinaryMessages.setMessageHandler for each of the channels to setup a handler of incoming messages from the platform that forwards to M2. Store the reply callbacks and configure R1 with a handler that invokes the correct one on receipt of a reply from the secondary isolate.
@mravn-google

This comment has been minimized.

Copy link
Contributor

mravn-google commented Jan 12, 2018

@Hixie I suggest moving to next milestone. A workaround seems to exist. A good solution will require some API design work and iteration.

@xuexin

This comment has been minimized.

Copy link
Contributor

xuexin commented May 22, 2018

I got the same problem, any news about this issue?

@realvotum

This comment has been minimized.

Copy link

realvotum commented Aug 23, 2018

@mravn-google Do you have any update on this? I ran across the same problem ( I'm in a need of generating e.g. RSA 2048 key pair on the platform side which takes a while on older devices or crypt/decrypt data). I'd rather avoid creating threads or services as that would double my work on the platform side since it is to be implemented for both Android and iOS platforms. Do we have any easy way to run those platform specific operations asynchronously from Dart? Your workaround seems to be fine, however it seems that I have some issues with implementing that as I am quite new to Flutter and Dart (I'm not sure how to configure a handler that forwards messages for an Isolate and so on), sorry about that.

@herrytco

This comment has been minimized.

Copy link

herrytco commented Dec 7, 2018

I got the same issue.

I need to generate a PDF on the Android/iOS side which takes a while and the compute(...) method crashes the app if i run call from there.

@aungmawjj

This comment has been minimized.

Copy link

aungmawjj commented Jan 5, 2019

I got this issue, and now i'm stuck.

I want to read accessToken, from shared preferences, in the background isolate, even though the app is not in the foreground (main isolate may not be running, i'm not very sure).

So, can someone tell me a solution to read persisted data from background isolate.
I'm really in trouble now.

@goderbauer

This comment has been minimized.

Copy link
Member

goderbauer commented Jan 23, 2019

/cc @bkonyi This seems related to something you are working on.

@Thomson-Tsui

This comment has been minimized.

Copy link

Thomson-Tsui commented Feb 1, 2019

I got the same issue. our product base on CPU-bound. we need to use platform method in isolate. Could you please tell us another way to solve this problem?

@rmawatson

This comment has been minimized.

Copy link

rmawatson commented Feb 20, 2019

@Thomson-Tsui I had a similar problem, and based on a distillation of the work by @bkonyi and his FlutterGeofencing sample I managed to get something to work in an isolate that I am using with flutter_blue and other plugins. Its pretty untested but please give it a try.

@goderbauer goderbauer removed the framework label Feb 20, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment