rmawatson / flutter_isolate

Launch an isolate that can use flutter plugins.
MIT License
261 stars 78 forks source link

flutter_reactive_ble can't be used using flutter_isolate? #91

Open ahoelzemann opened 2 years ago

ahoelzemann commented 2 years ago

Hey, I'm trying to run code that uses the flutter_reactive_ble library in an isolate using your plugin. However, the same code - that runs perfectly fine in the foreground - doesn't run in an isolate. When I went through the code with a debugger I saw that the package functionality is not provided.

The first thing I'm doing after initializing my BLEManager is to check the ble state of my own smartphone. The state is returned as unknown. Any ideas on how to make this package run using flutter_isolate?

thanks and best regards!

nmfisher commented 2 years ago

Is this on Android? If flutter_reactive_ble is like flutter_blue, it might not be possible to use with flutter_isolate (see https://github.com/rmawatson/flutter_isolate/issues/71).

Basically anything running under a Flutter isolate does not have an attached Activity, so any Flutter plugin that assumes the existence of an Activity just won't work.

ahoelzemann commented 2 years ago

Hey, thanks for the quick reply!

No, it happens on iOS. However, I figured out that it's just the BLE state of the smartphone that cannot be discovered and therefore remains unknown. I can ignore it, since the rest of the code runs in the isolate.

ahoelzemann commented 2 years ago

Hi @nmfisher ,

I'm not 100% sure, but for it seems a bit like flutter_isolate doesn't run properly in the background. I upgraded Xcode to version 13.1, because it was necessary to be able to work with flutter_reactive_ble. Since then I get the following warning:

[BackgroundTask] Background Task 2 ("Flutter debug task"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(:) for your task in a timely manner to avoid this._

I know that this is a message from the debugger that doesn't appear in the release version.

Two other things happen to me. Number one is that I'm not able to spawn new isolates from within a running isolate. Every object in that new spawned isolate stays null and cannot properly initialized. The second thing is that the app stops executing, and only continues working when I put the app in the foreground again.

My device is an iPhone X with iOS 15.0.2 installed. Btw, on Android everything runs smoothly.

nmfisher commented 2 years ago

Thanks @ahoelzemann, are you able to provide an example repository that demonstrates the bug?

ahoelzemann commented 2 years ago

Thanks for the quick reply. I can't give you access to the complete application, since it's a private repository, but I created another one with my two dart files that are important.

https://github.com/ahoelzemann/isolate_example

LandingScreen.dart:

Line 823/824: spawns the isolate and is followed by the listener that listens on the port for messages. Line 36: defines the method isolate1 which contains just one line of code that triggers an upload of data from a smartwatch to my smartphone.

BTExperimental.dart

Line 700: stopRecordingAndUpload() is executed in the background. This method creates an object of my BLEManager and executes all necessary methods to upload data. At the end of the method my data gets uploaded to a webserver.

The complete procedure takes up to 45 minutes, therefore I need to execute it in the background.

nmfisher commented 2 years ago

Thanks, I'll take a look over the next few days.

ahoelzemann commented 2 years ago

@nmfisher I digged a bit deeper and the problem that objects and variables were set to null was related to a bug in my code for discovering BLE devices - reported here https://github.com/PhilipsHue/flutter_reactive_ble/issues/446.

However, the isolate still stops working when the app is put in the background and my device gets disconnected.

I now spawn a main isolate (line 935 in LandingScreen.dart). This main isolate then spawns a worker isolate that deals with the file upload.

I have a listener set that listens to disconnections from the device (line 742, BTExperimental.dart) and notifies the main isolate to kill the worker isolate and spawn a new one that starts over the upload process (line 44, LandingScreen.dart).

Whenever this happens and the app is in the background, the main isolate is supposed to spawn a new worker.

The following log shows the problem a bit (take a closer look on the time stamp). A respawn should normally be executed after one minute.

Important are the following two lines:

At 14:33:49.407507+0100 the device has been disconnected (app is in the background), Then the app is in hibernate mode, even though the main isolate should continue running and spawn a new one after one minute.

At 14:37:07.796322+0100 I put the app again in the foreground and the execution continues immediately.

However the code runs fine, as long as the app stays in the foreground. It also terminates without any problems when the app is in the background and the device doesn't get disconnected (and therefore I don't need to spawn a another worker isolate).

14:33:40.181088+0100    Runner  flutter: startUpload()
14:33:40.181220+0100    Runner  flutter: =4
14:33:40.181313+0100    Runner  flutter: >
14:33:42.183798+0100    Runner  flutter: ble start upload command done /////////////
14:33:42.185045+0100    Runner  flutter: Status:Instance of '_ControllerStream<ConnectionStateUpdate>'
14:33:42.186110+0100    Runner  flutter: WAITING FOR 4 FILES, THIS WILL TAKE SOME MINUTES ...
14:33:42.687681+0100    Runner  flutter: 1 Start uploading ///////////////
14:33:42.761694+0100    Runner  flutter: Saving: d202111171019.bin
14:33:45.833573+0100    Runner  Received configuration update from daemon (initial)
14:33:49.407386+0100    Runner  flutter: ConnectionState for device 59A31B6A-36F9-8D73-9255-60CDE3C0261B : DeviceConnectionState.disconnected
14:33:49.407507+0100    Runner  flutter: LISTENER LISTENS TO CHANGE
14:33:49.414572+0100    Runner  flutter: BLE Connection closed - Killing the Isolate and spawning a new one.
14:33:49.415595+0100    Runner  flutter: Error unsubscribing from notifications: PlatformException(reactive_ble_mobile.Central.(unknown context at $1013570e4).Failure:1, The operation couldn’t be completed. (reactive_ble_mobile.Central.(unknown context at $1013570e4).Failure error 1.), {}, null)
14:33:49.424858+0100    Runner  {"msg":"CLLocationManager", "event":"activity", "_cmd":"dealloc", "self":"0x2827be600"}
14:33:49.425188+0100    Runner  {"msg":"state transition", "event":"state_transition", "state":"LocationManager", "id":"0x2827be600", "property":"requestingLocation", "old":0, "new":0}
14:33:49.425380+0100    Runner  {"msg":"state transition", "event":"state_transition", "state":"LocationManager", "id":"0x2827be600", "property":"requestingRanging", "old":0, "new":0}
14:33:49.426327+0100    Runner  {"msg":"state transition", "event":"state_transition", "state":"LocationManager", "id":"0x2827be600", "property":"updatingRanging", "old":0, "new":0}
14:33:49.426455+0100    Runner  {"msg":"invalidating client", "client":"0x125e821d0"}
14:33:49.426688+0100    Runner  {"msg":"invalidating client", "client":"0x125e821d0"}
14:33:49.427892+0100    Runner  {"msg":"state transition", "event":"state_transition", "state":"LocationManager", "id":"0x2827be600", "property":"lifecycle", "old":"0x280e956c0", "new":"0x0"}
14:33:49.432038+0100    Runner  {"msg":"client about to be destroyed", "client":"0x125e821d0"}
14:33:53.712428+0100    Runner  {"msg":"CLLocationManager", "event":"activity", "_cmd":"onDidBecomeActive:", "self":"0x2827a3830", "notification":"NSConcreteNotification 0x2825aad80 {name = UIApplicationDidBecomeActiveNotification; object = <UIApplication: 0x125d062d0>}"}
14:33:53.712692+0100    Runner  {"msg":"CLLocationManager", "event":"activity", "_cmd":"onDidBecomeActive:", "self":"0x2827b61e0", "notification":"NSConcreteNotification 0x2825aad80 {name = UIApplicationDidBecomeActiveNotification; object = <UIApplication: 0x125d062d0>}"}
14:37:06.132174+0100    Runner  [de.activate.tractomove] Getting pending notification requests (async)
14:37:06.188133+0100    Runner  [de.activate.tractomove] Getting pending notification requests (async)
14:37:06.188409+0100    Runner  [de.activate.tractomove] Getting pending notification requests (async)
14:37:06.271178+0100    Runner  [de.activate.tractomove] Got 0 pending notification [ hasCompletionHandler: 1 ]
14:37:06.340307+0100    Runner  [de.activate.tractomove] Got 0 pending notification [ hasCompletionHandler: 1 ]
14:37:06.353213+0100    Runner  [de.activate.tractomove] Got 0 pending notification [ hasCompletionHandler: 1 ]
14:37:06.409303+0100    Runner  #Warning Connection interrupted!
14:37:06.456461+0100    Runner  {"msg":"CLConnection::handleInterruption", "event":"activity"}
14:37:06.468735+0100    Runner  {"msg":"CLClientInterruptionHandler", "event":"activity", "client":"0x125e961f0"}
14:37:06.468940+0100    Runner  {"msg":"Sending cached messages to daemon", "event":"activity"}
14:37:06.469192+0100    Runner  [de.activate.tractomove] Getting notification categories (async)
14:37:06.627194+0100    Runner  Will expire BGTask activities: {(
)}
14:37:06.630630+0100    Runner  Will expire activities: {(
)}
14:37:06.631259+0100    Runner  Calling expiration handlers for activities: {(
)}
14:37:06.631489+0100    Runner  Calling expiration handlers for tasks: {(
)}
14:37:07.796322+0100    Runner  flutter: ble is available.
14:37:07.796401+0100    Runner  flutter: scanning for devices
14:37:08.095741+0100    Runner  flutter: Bangle.js 834b
14:37:08.095995+0100    Runner  flutter: [144, 5, 0, 0, 0, 0, 0, 0, 0, 0]
14:37:08.096240+0100    Runner  flutter: \^E
14:37:08.097269+0100    Runner  flutter: trying to connect....
14:37:08.097842+0100    Runner  flutter: Start connecting to 59A31B6A-36F9-8D73-9255-60CDE3C0261B
14:37:08.863404+0100    Runner  [VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: Bad state: Future already completed
#0      _AsyncCompleter.complete (dart:async/future_impl.dart:45)
#1      BluetoothManager._checkIfBLAndLocationIsActive.<anonymous closure> (package:trac2move/ble/BTExperimental.dart:141)
<asynchronous suspension>
14:37:09.833574+0100    Runner  flutter: ConnectionState for device 59A31B6A-36F9-8D73-9255-60CDE3C0261B : DeviceConnectionState.connected
14:37:09.833678+0100    Runner  flutter: connected! now discovering characteristics
14:37:09.834312+0100    Runner  flutter: LISTENER LISTENS TO CHANGE
14:37:11.836402+0100    Runner  flutter: not Sending
14:37:12.340251+0100    Runner  flutter: checkingOsVersion
14:37:12.940493+0100    Runner  flutter: setting time
14:37:14.953588+0100    Runner  flutter: Sending getNumFiles command...
14:37:20.042016+0100    Runner  flutter: [115, 116, 97, 114, 116, 85, 112, 108, 111, 97, 100, 40, 41, 13, 10]  //////////////
14:37:20.042154+0100    Runner  flutter: startUpload()
14:37:20.042340+0100    Runner  flutter:
14:37:20.058201+0100    Runner  flutter: [61, 52, 13, 10, 62]  //////////////
14:37:20.058924+0100    Runner  flutter: startUpload()
14:37:20.059098+0100    Runner  flutter: =4
ahoelzemann commented 2 years ago

@nmfisher , have you had time to look into the problem further?

nmfisher commented 2 years ago

@ahoelzemann - if I'm reading your issue correctly, you're saying that a running isolate cannot spawn a child isolate when the app is running in the background on iOS?

ahoelzemann commented 2 years ago

@nmfisher no, not exactly. Sorry for the misunderstanding. My very long running (up to 2 hours) isolate pauses. The function my isolates call contains several other function calls. I'm using BLE to connect to a smartwatch and upload data from the smartwatch to my phone. After this I want to upload my data to a server.

However, sometimes the isolates pauses after executing the data upload from the watch to the phone and doesn't execute the server upload.