-
Notifications
You must be signed in to change notification settings - Fork 27.4k
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
Plugins crash with "Methods marked with @UiThread must be executed on the main thread." #34993
Comments
Use activity.runOnUiThread |
And do you know why it changed, why it worked before? |
See this |
Thanks @ymback, the messages in flutter/engine#8830 really helped understanding the context.
|
Thanks for this. This change also broke at least one more of the flutter team's own plugins (android_alarm_manager), and probably would have been immensely frustrating to figure out how to handle on my own. |
…ed on the main thread." on Flutter stable channel(1.7.8) reference: flutter/flutter#34993
rxJava devs may find the familiar |
I have to keep running some code in my plugin in a separate thread for UI not to block, but when I try to run the code, I get How do I fix this? |
Experiencing same.. Downgrading flutter until it's fixed. |
Wow @ymback, nice link; missed it the first time. @Sitiritis and @michaelreed would likely appreciate the insight in the comments. tl;dr this change will not be reverted; it is protecting against indeterminant code that was likely crashing silently; it is improving your app, though it will not be obvious how, given that you are dealing with concurrency |
@AndruByrne, I don't quite get to use this insight. I am creating a thread in the Android plugin and then start it. What should I change? |
@AndruByrne, could you help me understand how I can use an isolate with UIThread execution? From my basic understanding of them, I cannot (which probably explains why the alarm manager plugin is broken as well). |
I am using rxJava and here my solution
|
First one from @vargavince91 works like a charm, but I did slightly shorter. public class BitcoinWalletPlugin implements MethodCallHandler, ... {
private Handler uiThreadHandler = new Handler(Looper.getMainLooper());
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
switch (this.channel) {
...
case CHANNEL_NAME:
// before
// bitcoinWallet.setOnStartupListener(() -> eventSink.success(true));
// after
bitcoinWallet.setOnStartupListener(() -> uiThreadHandler.post(() -> eventSink.success(true)));
break;
}
}
} @Sitiritis
Make sure you are creating handler like that
You don't need isolate here, just use public static void registerWith(Registrar registrar) {
activity = registrar.activity();
} |
@pavel-ismailov I'm using isolates to do work in a background thread. I cannot do execution on the UI thread as UI is a foreground process. |
Not sure if I understood you right, but looks like your schema is:
If so - answer is private final class EventSinkImplementation implements EventChannel.EventSink {
final AtomicBoolean hasEnded;
private EventSinkImplementation() {
this.hasEnded = new AtomicBoolean(false);
}
@UiThread
public void success(Object event) {
if (!this.hasEnded.get() && IncomingStreamRequestHandler.this.activeSink.get() == this) {
EventChannel.this.messenger.send(EventChannel.this.name, EventChannel.this.codec.encodeSuccessEnvelope(event));
}
}
@UiThread
public void error(String errorCode, String errorMessage, Object errorDetails) {
if (!this.hasEnded.get() && IncomingStreamRequestHandler.this.activeSink.get() == this) {
EventChannel.this.messenger.send(EventChannel.this.name, EventChannel.this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
}
}
@UiThread
public void endOfStream() {
if (!this.hasEnded.getAndSet(true) && IncomingStreamRequestHandler.this.activeSink.get() == this) {
EventChannel.this.messenger.send(EventChannel.this.name, (ByteBuffer)null);
}
}
} You will see |
I tried to implement a number of solutions from a number of threads but all show me some or the other error. I have been trying to build a music player and the plugin crashes when I play the music with Can anyone help me with the direction I should take. Is it a problem in the plugin's dart file or some problem in flutter itself? If the plugin's code can help I will update the with the code. Any assistance is appreciated |
@pavel-ismailov I really feel that this is a huge oversight from the flutter development team, as mentioned before, they even broke their own example application for developing background services with flutter. @matthew-carroll it would be great if you could chime in with some background as to why this change was made and if it will be reversed or fixed any time soon. Someone previously linked this commit flutter/engine@2c9e37c. |
@matthew-carroll is there a specific plugin in |
@MichealReed unfortunately, I don't have context around the linked file, but I'm not aware of any issue with using method channels with background isolates. The UI thread is an Android concept rather than a Flutter/Dart concept, so you manage it from the Android side. If you place your messaging code within With regard to @amirh No, I'm not aware of any further plugin issues. I tagged you because some earlier posts on this thread seemed to indicate continuing crashes, but if all the plugins have been fixed then I assume we're good. |
@matthew-carroll I will see if the maintainer of that repository can assist with a minimal code reproduction to open a new issue with. To elaborate, he found a work around using context that allowed him to create something similar to an activity with background execution cross platform via dart. Prior to this change, use of context was allowed, but from my basic understanding of android development, context does not provide functionality to run on the UI thread, so the change here blocks the use of that approach. |
@MichealReed please see this guide, you should be able to run code on the UI thread from anywhere: |
I have updated my flutter version, and issue got resolved. |
I'm going to mark this issue as closed. Presently:
If there is more work to be done on this particular issue, feel free to re-open. If similar, but distinct problems appear, please file new issues. |
Shameless plug: flutter-plugin-scaffold will protect you from this error (using the fix in OP), and other quirks faced when writing flutter plugins. |
@matthew-carroll |
@vikramkapoor it depends why the button isn't working. The UI thread still exists and still executes even when the screen is off, so long as your app is still running. In general the issue you're describing is probably about Android background execution more than it is about Flutter. Are you running a Service to keep your app alive? Have you verified that your app is receiving the Bluetooth signal before it tries to send that signal to Flutter? |
@matthew-carroll thank you for that answer as it makes me wonder why I moved bluetooth to background isolate in the first place if the UI thread executes when the screen is off. I have moved the bluetooth implementation back to the UI thread and it still works with the screen off. Flutter_blue plug-in that I am using doesn't seem to be running a service for Android background execution. |
@vikramkapoor Bluetooth is pretty difficult to get right. I'm not sure if you're dealing with standard Bluetooth or BLE, but I would recommend doing as much manual testing of edge cases as possible. I think an active audio connection probably lets your app keep priority so that it doesn't get killed, but with BLE I think you probably need an explicit foreground |
@matthew-carroll I am using the BLE iTag button with the flutter_blue plugin. The application is able to respond to BLE notification upon button presses with the phone screen off even after the timers in flutter app stop triggering. So far I have only tested on Android 4.3, 6.0.1 though. It works fine on iOS. I plan to implement a foreground service once I learn how to do that. |
这个解决方法很优雅,但是我觉得这个问题flutter框架该解决一下。 |
Solution: wrap the method invocation in |
worked for me thanx |
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of |
There are multiple plugins that are breaking with the error message
Methods marked with @UiThread must be executed on the main thread.
. (See issues: https://github.com/flutter/flutter/issues?utf8=%E2%9C%93&q=%5C%40UiThread).I didn't find any release notes, or guides for plugin authors to handle this issue (and to be honest, I don't 100% understand when it's necessary). Do you have any plans on updating the docs?
This issue is more like a question, or docs/release notes improvement request, and the examples below could serve for people who face the same issues. Also, I'm on
dev
channel, so maybe the issue (docs, release notes) can be fixed before you release tostable
.Crash
Versions
How to solve to issue
I found some fixes already in the wild, so I just had to tweak the
MethodChannel.Result
solutions to be able to fix theEventSink
issues.The issues + solutions I found are here:
The code examples in this issue use Java and prefer clarity over lines of code. If you use Kotlin, a less verbose option might be possible.
They solved it by wrapping the
MethodChannel.Result result
with a "handler+looper+runnable combo":I was working with an
EventChannel.EventSink eventSink
, so I had to patch my plugin, too. The solution for the event sink was similar:The text was updated successfully, but these errors were encountered: