Calling top-level async function from different Isolate - flutter

Unable to call a top-level async function from another isolate, an example would be trying to access SharedPreferences/Document-path value from a different isolate since getting the instance would require 'awaiting'.
In my case, I am using flutter_downloader, downloads in another isolate, as soon as the download completes an encryption method is called which saves the result in a document path, path_provider's getApplicationDocumentsDirectory() returns a future which requires awaiting. I have made the encrypt function async and it is never getting called using both compute and Isolate.spawn so that it computes the encryption in another isolate.

The problem is probably that there are no plugins available in isolates, thus path_provider and shared_preferences don't work when called from the isolate.
If you need the getApplicationDocumentsDirectory() then you can determine that path before you start the isolate and hand it to the isolate as a message. This way you have the path available in the isolate and can save the file there. Accessing shared_prefs is not really possible, you can only do that after the isolate completes.
There are some projects like https://pub.dev/packages/flutter_isolate that try to make plugins available in isolates. You can explore those and see if it fits your needs.
Specifically for flutter_downloader there is an example in the README that shows how to communicate between the background isolate that handles the download callback and the main isolate of your app.
Basically you need to pass a message to the main isolate like PleaseGiveMeTheApplicationDocumentsDirectory and then answer the message with HereYourGoThisIsTheApplicationDocumentsDirectory.
You can use IsolateNameServer.lookupPortByName to get the SendPort of the main isolate and pass it the SendPort of the background isolate and then answer with the correct path. Once both sides know where to send messages to, you can pass whatever you want, you can also pass a message that triggers some shared_preferences usage in the main isolate.

Related

Flutter SIP call from background service

I'm making a Flutter SIP call application using dart-sip-ua https://github.com/flutter-webrtc/dart-sip-ua.
I need to use PUSH messages to wake up the application and set the call.
I was able to wake up the application, but how to connect with the current call?
According to the documentation, when making a call, a Call object is created and it has an answer() method.
But when the Call object wakes up, we do not have, respectively, the answer() method either.
I tried to find a solution using SIPUAHelper.findCall(callUUID), but without success: callUUID is generated when the call is initialized, and we do not know it in advance to pass it via PUSH.
That is, I show my CallScreen, but I don’t have a Call object...
Is there really only one option? Wait until the SIP connects itself? It could be 5, 10, 30 seconds...
Help me please. What is the approach to accomplish this task? I feel that my approach is not correct.
Thank you!
One possible solution to your problem could be to store the Call object in a persistent storage, such as a database or a file, whenever it is created. Then, when the application wakes up and receives the PUSH message, you can retrieve the Call object from the storage and pass it to the CallScreen. To do this, you can use a database library like "sqflite" or a file library like "path_provider" to store and retrieve the Call object as a serialized object.
Another option could be to use the "callUUID" of the Call object as a key to store and retrieve it from the storage. You can pass the "callUUID" via PUSH message, and use it to retrieve the Call object from the storage. However, you need to make sure that the "callUUID" is unique and persistent across sessions, so you may need to use a combination of the SIP call ID and the local session ID.
A third option could be to use a global state manager like "Provider" or "Redux" to store the Call object and share it across the application. This way, you can access the Call object from any widget, even if it is not passed directly as an argument. However, you need to be careful with the performance and complexity of using a global state manager, especially if you have a large and complex application.
I hope these suggestions can help you solve your problem

How to add periodic task in compute function of dart?

I want to send log to server periodically after 30 sec.
For performance i want to use different thread by using compute function.
But Timer is not working in compute.
Any suggestions to do task in different threads periodically in flutter?
You could just use an Isolate directly. It's what compute does under the hood.
However, I don't think sending information to a server would block your UI thread too much.
On top of that, if you're using app state to determine the log message I would just keep it in the main Isolate.
I would probably wrap the (Material|Widget|Cupertino)App in a StatefulWidget and add a Timer.periodic in the initState.
You also have to note communicating with an Isolate means your message has to be copied to its memory. While sending an HTTP Request to a server is usually async and non-ui-blocking.

ObjectBox Dart/Flutter multi-isolate access

Creating a separate thread for a question stated in a comment...
How does ObjectBox handle concurrent(by different threads/isolates) write requests? example of my use case: FCM "onBackgroundmessage" call runs in a different isolate, the same time multiple write requests might happen. "Hive" is failing in this case completely. Is there any inbuild solution in ObjectBox?
ObjectBox for dart is based on a native ObjectBox core library that handles concurrency using Transactions. Let me pick out few points relevant to the question:
Transactions manage multi-threading; e.g. a transaction is tied to a thread and vice versa.
Read(-only) transactions never get blocked or block a write transaction.
There can only be a single write transaction at any time; they run strictly one after the other (sequential).
As for the isolates in Dart/Flutter - yes, they can safely access the same store, you just need to make sure it really is the same native store instance. To do so, you do the following steps:
Create a Store() instance in your main isolate, as you normally would.
Get ByteData store.reference which contains the information about the native store.
Send this reference to another isolate, via a SendPort.
In another isolate, receive the reference and open a local Store instance, using Store.fromReference(getObjectBoxModel(), msg as ByteData).
Now both isolates have their own Dart Store instances which internally use the same underlying native store. Therefore, their transactions are synchronized, they both see the same data and get notifications on data changes. 🎉
You can see the code I've just described this test case: https://github.com/objectbox/objectbox-dart/blob/461a948439dcc42f3956b7d21b232eb9c2bc26e1/objectbox/test/isolates_test.dart#L50
Make sure you don't close the store while another isolate is still using it. Better not close it at all - that's best practice in normal applications without huge amounts of background work.

Is there any way to get Documents Directory synchroniously?

I need to load a small settings file before showing the first screen.
I'm curently using the PathProvider plugin, but all the functions in it are async.
Is there any way to find the application documents directory synchroniously and then use dart:io sync functions on it? Instead of showing a placeholder for infinite decimal of a second.
EDIT: considering the first 2 answers, probably my explaination was really bad. The answers suggested me to still use async code. Problem is, I need the directory before runApp(), and I dont want my void main() to be async, because this ruins any splash animation for sure.
And the PathProvider API is async, as far as I can see, only because it utilizes MethodChannel, which is async, but functions behind it in PathProvider are pretty much synchronious.
What I'm asking is - is there a way, on Android or IOS, to know ApplicationDocumentsDirectory in sync code? Maybe without using platform-specific code at all?
You can use FutureBuilder for the same purpose without having to worry about execution.
It will allow you to do your process in the background and allows you to show a splash screen UI until your specified process is complete and data is ready to use.
Note: Any plugin or code which needs access to the device's native method channel will be async by default, since access of the method channel itself is supposed to be async.

Why it must be use the await for save/get local data in Flutter?

I want to save data to local(just a few data), and I find several package for do that (e.g shared_preferences,secure_storage,sqflite), but all of them are need to use await (Future), if I use these, I have to change my existing codes for wrap in Future, but I just feel that's very troublesome, so I am wondering why all of these are need to use await for save data? or can I use another easy way to do that?
Thanks!
To be clear: It is impossible to have a synchronous system call in flutter.
This is due to an architectural decision: Instead of having the language bridge, Flutter uses two bus (one dart, one native) sending messages to each other.
This is faster than by using a bridge but enforce asynchronous messages.
await as the name suggest wait for some event without stopping following lines of code to execute because that work will take some milliseconds to perform. So, it's a good idea to use await which needs to have async in the function.
There is other way of doing this work without using async-await.
That's then().
So, you go with this without having to add async to your function.
performWork().then((result) {}));
If you do not wish to use await you can call then() on the Future object. Refer this link for more details.
If you are working with Flutter you will definitely have to handle Future's there is no work around it.