Why does MethodChannel.Result.success() sometimes cause a timeout at Flutter? - flutter

I'm having a simple Flutter plugin that communicates through a MethodChannel with some native Android code. Here's a simple example how the methods provided by the plugin look like:
Future<void> doSomethingImportant(int address, SomeImportantData importantData) async {
String jsonButStillImportant = jsonEncode(importantData.toJson());
return methodChannel.invokeMethod('doSomethingImportant', {"address": address, "data": jsonButStillImportant});
}
When the doSomethingImportant() (java) method is done, it delivers the result to Flutter by calling:
public void onSuccess(Result result, Object value) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
result.success(value);
Log.d("Debug", "Why does this occasionally cause a timeout at Flutter?!?");
}
});
}
Then, during tests, the method is called like this:
void _someTestMethod() async {
await doSomethingImportant(anAddress, theMostImportantDataInTheWorld).timeout(const Duration(seconds: 1)).catchError((e) {
print("Something's wrong here: "+e.toString());
return;
});
}
This is working well most of the time. Sometimes, however, the test fails because doSomethingImportant() runs into a timeout, and the following shows up at logcat:
2021-12-15 19:56:50.919 D/Debug: Why does this occasionally cause a timeout at Flutter?!?"
2021-12-15 19:56:51.697 I/flutter: Something's wrong here: TimeoutException after 0:00:01.000000: Future not completed
Now I'm wondering what can possibly cause the timeout and how to prevent it. What's noticeable is that the timeout message always appears about 700-800 milliseconds after result.success() was called.
Edit 1:
The problem is that sometimes the future does not complete, even though result.success() is called. This happens only sporadically, but if it does, the timeouts purpose is to gracefully exit the calling function. Simply removing the timeout is unfortunately not an option, because it would block forever.
Edit 2 - some more background information:
Please note that the above code snippets of doSomethingImportant() and _someTestMethod() are just examples to illustrate how the methods look like. (Except onSuccess(), this one's an exact copy from the production code).
The actual plugin controls a Bluetooth Mesh network. _someTestMethod() sends and receives data to verify that the mesh network and the firmware running on the individual nodes are doing what they are supposed to do. The logic in _someTestMethod() looks like this:
Send data into the mesh.
Read data from the mesh.
Verify that the read data matches the expected result.
The await/timeout() approach has been chosen because each step depends on the successful completion of the previous step. Nesting those calls in .then() callbacks would be an alternative, but I simply prefer the top-to-bottom await/timeout()-spaghetti-code to nested .then() callbacks.
The problem is, that every now and then, MethodChannel.Result.success() is called at Android, but Flutter does not complete the Future. If that happens, the Future stays uncompleted for all of eternity and logcat does not tell anything neither.
Again, this happens only sporadically. Most of the time it is working just fine.
Now the question is, what's wrong here? Is there any other way to convince the Future to complete? Any ideas are welcome Thank you!

Now I'm wondering what can possibly cause the timeout and how to prevent it. What's noticeable is that the timeout message always appears about 700-800 milliseconds after result.success() was called.
Do you have any ideas how to fix (or even debug) the source of this timeout? Thank you!
You are using .timeout(const Duration(seconds: 1)), so it times out. If you don't want it to timeout, remove that code.
Can you clarify what the issue you are facing is? Are you dissapointed that your work is not completed within 1 second, or have you just not realized you added the timeout?
After your edit:
Using await + timeout does not gracefully exit the function, it literally throws an error. To "gracefully" handle this situation where a Future might not immediately complete, instead of using await, use .then instead. That way, any code below your platform method invocation won't be delayed. Of course, you won't have the result in your platform method. If you need that result immediately, then neither await + timeout + catchError nor .then will help you.
Arbitrarily choosing 1 second (or any other time) to run the code and then throw an error is guaranteed to bring issues, especially in a production app with users using all sorts of devices. On slow devices, this could legitimately happen, you should handle it gracefully. It might also hurt debugging, lets say you make a platform method invocation and then pause at a breakpoint, the code will throw the error, because the timer still runs. What an awful debugging experience. I highly recommend not using timeout in this case.
To help debug:
Open the android project window in Android Studio, and either:
Run the debugger from this window (which launches the Android app) with the debugger attached
Run the debugger from Flutter window, and attach the android project window debugger to your running app.
app
Place a breakpoint on line result.success(value);, and observe if it is being called.
Perhaps new Handler(Looper.getMainLooper()).post(new Runnable() { is never being called, in that case you have to understand why that runnable is never posted. I'm not sure if you actually ran your example posted above, or if you're just trying to debug a more complex application.
Perhaps another issue is happening, and a more useful error could be shown. (You didn't share many logs, check out logcat or pidcat.). A wild guess would be that the Main thread is busy right now, doing some work that you shouldn't have made it do on the main thread 😅🙈, or just doing Flutter work (less likely).

Related

VSCode exits while debugging a flutter app

It seems like vscode is trying to present data in the Locals and/or Watch for a large object but after about 10 seconds, it will kill the app and give the message "Exited (sigterm)" in the debug console. I can pinpoint it to one example where I break on a line immediately after this line:
Uint8List inputBytes = Uint8List.fromList(List.filled(100000000, 0));
I can see "Locals" spinning around but nothing happens and then the app terminates. Is there a setting that can prevent this somehow? Maybe it can cap the represented data at a certain length instead of trying to print it all out?
(I believe this is a vscode specific problem because when I repeat these steps in Android Studio, it doesn't have this issue)
Thanks.
This is a bug in the debug adapter. It should know this is a list and fetch a paged view of the elements, but it's currently fetching them all. For me it doesn't crash, but it also doesn't respond within several minutes.
I'm working on a fix that will let VS Code page through the data:
This will ship in an upcoming Dart/Flutter SDK release.

How do I catch an error in Flutter (`beacon_broadcast` library)? Conventional methods don't work for this library

I am using a library called beacon_broadcast when I start the beacon broadcast, it outputs an error in the console, but I'm not able to catch it (try, .catchError, and using .then doesn't work).
Console Output:
D/BeaconParser( 7318): Parsing beacon layout: blah_blah_blah
D/BluetoothAdapter( 7318): isLeEnabled(): ON
D/BluetoothAdapter( 7318): isLeEnabled(): ON
E/BeaconTransmitter( 7318): Advertisement start failed, code: 2
Error code 2 means advertising slot not available.
I do not think that I need to show my source code for this, since this does not seem to be a problem with my code, but rather a question about how to do something under specific circumstances.
Check if your try clause fully wraps the portion of code responsible for the error but I suspect that this is not something which you can solve now.
Looking at the library's repository I can see that it needs to handle first the errors that occur in platform-specific code (in this case Android/iOS/Web) and then that concern with Flutter. In particular, the error should propagate from the platform to Flutter to let him handle and eventually raise the exception. Sometimes developers forget these checks and this is why you cannot handle the exception directly from dart.

Confusing error from libasound function snd_pcm_hw_params

My question has to do with the libasound function named "snd_pcm_hw_params" in connection with code to play a sound file. I am new to ALSA programming. Using a coding example I found on the internet, I wrote a small program to play a 7 second .wav file to the default sound card. When I run this code several times in a row, occasionally (but not always) the requisite call to snd_pcm_hw_params to write the previously filled in snd_hw_params_t struct to the driver, I get back an error code of -2 (ENOENT). I have no idea what this means, nor how to handle nor prevent it. My code just emits an error message and bails. Usually, if I run it again, the code runs fine. Its fine for my use, but eventually, this code is supposed to be given to a non-programmer to use, and I'd like to either prevent the error, or resolve it internally without involving said non-programming user. I note hear that the user is supposed to be able to cause an early abort of the program by clicking a button, and when this happens, my code calls snd_pcm_drop, followed by snd_pcm_close. If the program runs to completion, and plays all 7 seconds of the wav file, then it finishes up by calling snd_pcm_drain, followed by snd_pcm_close. Any help or suggestions would be greatly appreciated. :)

How to define on spawn handler for Mojo::Server::Prefork?

I have simple web application written in perl/Mojolicious and running under hypnotoad.
I need to define some handler for the "spawn" event (emited by Mojo::Server::Prefork).
But i dont know, how to insert this hander definitiion in the code of startup method of Mojolicious application. $self->on("spawn"=>sub {}) doesnt work :( And Dumper($self) was not helpful at all: there are no $self->server or $server->prefork ...
Tell me please, how to do it.
Thanks!
Although i still dont know how to define handler fired on process "spawn", i can tell that absolutely the same thing can be done by using
Mojo::IOLoop->singleton->next_tick(sub {
doingSomethingOnProcSpawn()
});
As it described in Mojolicious Cookbook (http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Pre-forking):
During startup your application is preloaded in the manager process,
which does not run an event loop, so you can use "next_tick" in
Mojo::IOLoop to run code whenever a new worker process has been forked
and its event loop gets started.
Hint: As i see in my real application, Mojo::IOLoop->singleton->next_tick and Mojo::IOLoop->next_tick works absolutely identically, so i dont know what is the difference between them.

GWT Selenium Tests sometimes fails

we currently have a nice problem with our selenium tests in a gwt powered gui.
The application contains two sections (filter and grid). Our tests sometimes fails with a NoSuchElementException.
Crazy is the following: I stop the test in eclipse with a breakpoint and inspect the page with firefox firebug or any other addon. And okay - I cannot find the desired element. But (without restarting the application or any other changes in eclipse), if i try again and search the element it is there and a resume in eclipse the test goes green. For me it seems like a synchronize problem in firefox.
A explicitly wait command
new WebDriverWait(getDriver(), 10).until(condition);
has the only effect, that the timeout (10 seconds) happens.
As I said - sometimes the test is green and sometimes it fails.
Has anybody an idea?
Sounds like you load some data asynchronous (RPC) from the server? The data and thus the element which presents the data in the UI is not there yet, when Selenium is looking for it. Depending on how long your queries take on the database or what latency you have on the network the wait time may vary from test-run to test-run.
I have a workaround for this problem and would share this.
The following piece of code is executed before the explicit wait command is running.
Window window = getDriver().manage().window();
Dimension dimension = window.getSize();
Dimension tmp = new Dimension(dimension.getWidth() - 1, dimension.getHeight());
window.setSize(tmp);
window.setSize(dimension);
I figured out, that the DOM is in "synchronized" state after the browser window is resized. So I decrement the width and than set it back to the old value.
If anybody has a better suggestion - let us know ;-)