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

Possible issue: channel sent a message from native to Flutter on a non-platform thread #4

Open
jakusb opened this issue Oct 22, 2023 · 4 comments

Comments

@jakusb
Copy link

jakusb commented Oct 22, 2023

I see below message in my log. It seems a method needs to be wrapped in something like:
dispatch_async(dispatch_get_main_queue(), ^{});

[VERBOSE-2:shell.cc(1004)] The 'sstonn/flutter_watch_os_connectivity_callback' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel.
See https://docs.flutter.dev/platform-integration/platform-channels#channels-and-platform-threading for more information.

@wolfulve
Copy link

wolfulve commented Oct 30, 2023

I ran into the same warning/issue indicated in the previous message, along with one crasher issue.

I affected the followings modifications in a local working copy:

  1. I wrapped all the callbackChannel.invokeMethod() calls with DispatchQueue.main.async {} That said, I believe only a few cases were causing the issue, e.g., calls made while not on main thread..
  2. fixed an illegal access (crasher issue), related to threading, that occurred in the replyMessage code block (within the function handle() switch statement). This fix was affected by wrapping all the code within:
  public func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping (`[String : Any]) -> Void) {
  ...
  ...
  }
  

which is not called on the main thread, with DispatchQueue.main.async {}

  1. lastly, I added code to remove the replyhandlerId from the map after the replyHandler is called, e..g, in the switch statement block for replyHandler, e.g.,
replyHandler(message)
messageReplyHandlers.removeValue(forKey: replyHandlerId) // added this line

Not causing an issue, but, there is no need to keep the ids in the map after the reply handler function has been called.

Note: since making the above changes, I've not seen any further issues.

@wolfulve
Copy link

wolfulve commented Dec 7, 2023

I also made the following changes to: ios/lib/watch_os_observer.dart (which was failing)


             .add(ActivationState.values[activateStateIndex]);
         break;
       case "pairDeviceInfoChanged":
-        Map<String, dynamic> rawPairedDeviceInfoJson =
-            (call.arguments as Map? ?? {}).toMapStringDynamic();
+        Map<String, dynamic> rawPairedDeviceInfoJson = {};
+        try {
+          rawPairedDeviceInfoJson =
+              (jsonDecode(call.arguments) as Map<String, dynamic>? ?? {})
+                  .toMapStringDynamic();
+        } catch (e) {
+          pairedDeviceInfoStreamController.addError(e);
+        }
         if (rawPairedDeviceInfoJson["error"] != null) {
           ///* Emit error and return
           pairedDeviceInfoStreamController


           WatchOsPairedDeviceInfo pairedDeviceInfo =
               WatchOsPairedDeviceInfo.fromJson(rawPairedDeviceInfoJson);
           pairedDeviceInfoStreamController.add(pairedDeviceInfo);
-        } catch (e) {
-          pairedDeviceInfoStreamController.addError(e);
+        } catch (ee) {
+          pairedDeviceInfoStreamController.addError(ee);
         }
         break;
       case "messageReceived":

@wolfulve
Copy link

wolfulve commented Dec 7, 2023

Code is switch now appears as:

case "pairDeviceInfoChanged":
     Map<String, dynamic> rawPairedDeviceInfoJson = {};
     try {
       rawPairedDeviceInfoJson =
           (jsonDecode(call.arguments) as Map<String, dynamic>? ?? {})
               .toMapStringDynamic();
     } catch (e) {
       pairedDeviceInfoStreamController.addError(e);
     }
     if (rawPairedDeviceInfoJson["error"] != null) {
       ///* Emit error and return
       pairedDeviceInfoStreamController
           .addError(Exception(rawPairedDeviceInfoJson["error"]));
       return;
     }
     try {
       ///* Map raw map to [PairedDeviceInfo] object
       WatchOsPairedDeviceInfo pairedDeviceInfo =
           WatchOsPairedDeviceInfo.fromJson(rawPairedDeviceInfoJson);
       pairedDeviceInfoStreamController.add(pairedDeviceInfo);
     } catch (ee) {
       pairedDeviceInfoStreamController.addError(ee);
     }
     break;

@chadpav
Copy link

chadpav commented Jan 5, 2024

@wolfulve any chance you might submit a PR to the repo? I came here because of the same issue.

I submitted a PR for the original issue for this post. You had some additional fixes in the code that I don't use so I'm hesitant to include those in my PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants