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

[Help]: Is there support for connection in isolates? #683

Closed
1 task done
enricocaliolo opened this issue Nov 14, 2023 · 19 comments
Closed
1 task done

[Help]: Is there support for connection in isolates? #683

enricocaliolo opened this issue Nov 14, 2023 · 19 comments
Labels
help Questions, help, observations, or possible bugs

Comments

@enricocaliolo
Copy link

Requirements

  • I've looked at the README 'Common Problems' section

Have you checked this problem on the example app?

No

FlutterBluePlus Version

1.12.13

Flutter Version

3.13.1

What OS?

Android

OS Version

Android 10

Bluetooth Module

Esp32

What is your problem?

Trying to connect to a device inside a different isolate results in this error:

'package:flutter/src/services/platform_channel.dart': Failed assertion: line 530 pos 7: '_binaryMessenger != null || BindingBase.debugBindingType() != null'. Cannot set the method call handler before the binary messenger has been initialized. This happens when you call setMethodCallHandler() before the WidgetsFlutterBinding has been initialized. You can fix this by either calling WidgetsFlutterBinding.ensureInitialized() before this or by passing a custom BinaryMessenger instance to MethodChannel().

The suggestion on the message error did not work. I want to ask if the current version supports connection via isolates, as I would need to refactor a bit of code to align my code with it, so knowing beforehand could save me a lot of trouble. Thanks.

Logs

'package:flutter/src/services/platform_channel.dart': Failed assertion: line 530 pos 7: '_binaryMessenger != null || BindingBase.debugBindingType() != null'. Cannot set the method call handler before the binary messenger has been initialized. This happens when you call setMethodCallHandler() before the WidgetsFlutterBinding has been initialized. You can fix this by either calling WidgetsFlutterBinding.ensureInitialized() before this or by passing a custom BinaryMessenger instance to MethodChannel().
@enricocaliolo enricocaliolo added the help Questions, help, observations, or possible bugs label Nov 14, 2023
@chipweinberger
Copy link
Owner

chipweinberger commented Nov 14, 2023

I've never tried it! feel free to submit a PR

I would think it would just work.

@chipweinberger chipweinberger changed the title Is there support for connection in isolates? [Help]: Is there support for connection in isolates? Nov 14, 2023
@chipweinberger
Copy link
Owner

chipweinberger commented Nov 14, 2023

Are you doing this in your isolate?

void isolateFunction() async {
  // Ensure that the Flutter engine is initialized
  WidgetsFlutterBinding.ensureInitialized();

  // isolate code
}

@chipweinberger
Copy link
Owner

Screenshot 2023-11-14 at 12 45 05 PM

@chipweinberger
Copy link
Owner

did you fix it?

@enricocaliolo
Copy link
Author

enricocaliolo commented Nov 16, 2023

Hi, sorry for the late reply. Using `WidgetsFlutterBinding.ensureInitialized' did not work. It gave the error: ' UI actions are only available on root isolate.' I also am not sure how I would implement a custom BinaryMessenger between the package and my app, so I haven't tested that.

For more context, I have a self-package outside the main app that handle the bluetooth communication between the app and the ESP I am using, as I need it for more than one app. I tried testing with the app example, but this package is far behind, so I would need to update it. This would be a lot of work, as the API changed a lot since I started, and it may won't even work, so I am deciding what to do.

Just tried with the example app (with the current version) to see if it worked, and it doesn't. When I use a function to connect to a device via compute(), it gives me the same error as it did previously.

Future<bool> connect(BluetoothDevice device) async {
  try {
    await device.connect();
    return true;
  } catch (e) {
    print(e);
    return false;
  }
}


try {
          WidgetsFlutterBinding.ensureInitialized();
          result = await compute(connect, lastDevice);
        } catch (e) {
          print('ERRO: $e');
        }

The actual functionality is a lot more complex than that, but it fails on the initial step of the connection. When I don't use isolates, it works as expected, but when I am on a custom isolate, it breaks and give the error mentioned previously.

@chipweinberger
Copy link
Owner

chipweinberger commented Nov 16, 2023

WidgetsFlutterBinding.ensureInitialized(); should be in the new isolate. Not the current isolate. (however this may not work still: flutter/flutter#10647)

Future<bool> connect(BluetoothDevice device) async {
  WidgetsFlutterBinding.ensureInitialized();
  try {
    await device.connect();
    return true;
  } catch (e) {
    print(e);
    return false;
  }
}

try {
    result = await compute(connect, lastDevice);
  } catch (e) {
    print('ERRO: $e');
  }

second, you should probably use Isolate. spawn(), and not use compute.

@chipweinberger
Copy link
Owner

chipweinberger commented Nov 16, 2023

see here: https://docs.flutter.dev/platform-integration/platform-channels#using-plugins-and-channels-from-background-isolates

Plugins and channels can be used by any Isolate, but that Isolate has to be a root Isolate (the one created by Flutter) or registered as a background Isolate for a root Isolate.

The following example shows how to register a background Isolate in order to use a plugin from a background Isolate.

import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';

void _isolateMain(RootIsolateToken rootIsolateToken) async {
  BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
  FlutterBluePlus.startScan();
}

void main() {
  RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
  Isolate.spawn(_isolateMain, rootIsolateToken);
}

@enricocaliolo
Copy link
Author

WidgetsFlutterBinding.ensureInitialized(); can't be used outside the main isolate, so I ruled that out. Now, I tried setting the custom BinaryMessenger, but it still give me the same error, that I need to use WidgetsFlutterBinding.ensureInitialized(); or passing a custom BinaryMessenger instance to MethodChannel().

Well, I'll probably just update the package and refactor the code, as I delayed it enough, and then see what I can do. I'll probably report it back here in a few days. Thanks for the replies!

@chipweinberger
Copy link
Owner

you need to use BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);

@chipweinberger
Copy link
Owner

did you try it?

@ichengao
Copy link

it seems caused by _methods.setMethodCallHandler(_methodCallHandler); only can working on main isolate

@enricocaliolo
Copy link
Author

Sorry for the late reply. Just updated the lib, but, unfortunately, it still don't work. I am passing the RootIsolateToken and setting BackgroundIsolateBinaryMessenger, but it still gives me the same error. At least in my app, I am not sure if this would work in the example either.

@chipweinberger
Copy link
Owner

is all of your FBP code in the isolate?

mixing and matching would cause problems i think

@enricocaliolo
Copy link
Author

No. It is on a different flutter project though, it may be related to this. I was passing the RootIsolateToken through the classes I use, dunno if this is optimal or not but it was the only way.

The only code I had on isolates was the connection. I need to confirm some characteristics to assert whether the device is a specific-one for my app or not.

When I have time, I can try putting an Isolate in the example app and see if it works, if you have not already, and then we can confirm for sure if this is a problem on my end or something related to the lib itself.

@chipweinberger
Copy link
Owner

chipweinberger commented Dec 2, 2023

I'm going to close this. To get isolats to work all you need to do:

  1. put all FBP code in isolate
  2. use BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);

if this doesn't work, please open a new issue.

@BuzzBumbleBee
Copy link

@chipweinberger im having the same sort of issues. Its down to a flutter limitation in setMethodCallHandler this calls setMessageHandler

https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/platform_channel.dart#L552

This is not supported with background isolates

More info :
https://api.flutter.dev/flutter/services/BackgroundIsolateBinaryMessenger/setMessageHandler.html

Im unsure if this is something flutter_blue_plus can do while using setMethodCallHandler to have the platform code call back to flutter

@BuzzBumbleBee
Copy link

Relates to this issue raised against core flutter

flutter/flutter#119207

@chipweinberger
Copy link
Owner

chipweinberger commented Jan 16, 2024

@BuzzBumbleBee
Copy link

@chipweinberger does that work ? I read the flutter engine code and all messages from calling methodChannel.invokeMethod always lands the response on the root isolate

You could have the UI isolate forward the message on to your background isolate, but that probably defeats the point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help Questions, help, observations, or possible bugs
Projects
None yet
Development

No branches or pull requests

4 participants