Open madsane29 opened 2 years ago
The Flutter app example uses a DriftIsolate
, does that help you regarding the setup?
Do I just randomly call into this db from main thread's method middle part and it will run next code on a new thread?
That is the idea, yes. Drift has an asynchronous API and will schedule the operation in the background isolate when you cal it in the main isolate. That is the whole idea of Drift isolates. I will make that clearer in the documentation and also link to the example.
I have a background service where I update data and the change is not reflected in the UI, I am using stream with .watch(), any clue?
I have a background service where I update data and the change is not reflected in the UI, I am using stream with .watch(), any clue?
Same question.
The app received the FCM, called the API and inserted data into database on background (app was not terminated) then back to app, app not trigger watch()
to update UI
Hello @simolus3 can you provide any solutions to make drift work in the background service? https://pub.dev/packages/flutter_background_service
@simolus3 Got same problem here, any solution? I opened an issue for emphasis the priority
Agree, the Isolates documentation doesn't yield enough information to make Streams across isolates work. Also seems outdated in regards to Flutter APIs (e.g. RootIsolateToken.instance may return null).
Do they just not work at all for you? It's a bit surprising if isolates work but streams don't since they use the same protocol - do you have more information about what you've tried? Maybe you're running into a bug, but we have tests for stream updates across isolates.
Also seems outdated in regards to Flutter APIs (e.g. RootIsolateToken.instance may return null).
Thanks, fixed in ca7f177ed94b8482248f024e73d79b3a05d96f2b. We can't depend on Flutter when building the docs so it's not easy to keep up with all Flutter API changes.
@simolus3 thanks for your reply!
So I initialize Drift as follows (from the main Isolate as well as from the background one):
static Future<DriftIsolate> createIsolateWithSpawn() async {
final token = RootIsolateToken.instance;
return await DriftIsolate.spawn(() {
if (token != null) {
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
}
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final path = join(dbFolder.path, 'mydb.sqlite');
if (Platform.isAndroid) {
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
}
final cachebase = (await getTemporaryDirectory()).path;
sqlite3.tempDirectory = cachebase;
return NativeDatabase(File(path));
});
});
}
Then, I create the database with
final isolate = await MyDatabase.createIsolateWithSpawn();
_db = MyDatabase(await isolate.connect());
This is what I gathered from the documentation might be the correct way for being able to use streams across isolates (plus the null check on RootIsolateToken, as mentioned above).
The background Isolate is used to write into the database. In the foreground, I use watch
to monitor changes on a table. However, the Stream in the foreground Isolate doesn't emit the data that is written into the database at runtime. It will however emit the inserted data after a restart of the app.
So far, I tested on Android only.
Ah, I think I got it now. I modified my database creation by storing/retrieving the DriftIsolate SendPort in IsolateNameServer:
static Future<DriftIsolate> createIsolateWithSpawn() async {
final token = RootIsolateToken.instance;
final isolate = await DriftIsolate.spawn(() {
if (token != null) {
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
}
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final path = join(dbFolder.path, 'mydb.sqlite');
if (Platform.isAndroid) {
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
}
final cachebase = (await getTemporaryDirectory()).path;
sqlite3.tempDirectory = cachebase;
return NativeDatabase(File(path));
});
});
IsolateNameServer.registerPortWithName(isolate.connectPort, 'drift_connect_port');
return isolate;
}
and
static Future<DriftIsolate> createIsolateFromPort() async {
final port = IsolateNameServer.lookupPortByName('drift_connect_port');
return DriftIsolate.fromConnectPort(port!);
}
From the main Isolate, I call
final isolate = await createIsolateWithSpawn();
_db = MyDatabase(await isolate.connect());
and, after the above, from the background Isolate
final isolate = await createIsolateFromPort();
_db = MyDatabase(await isolate.connect());
Not sure if this is by the book, but the main isolate stream emits data now. :)
Yeah that sounds correct to me - if you call DriftIsolate.spawn
multiple times, you'll get two independent isolates that won't talk with each other to update streams. Using name ports to coordinate isolate creation is a correct solution :+1:
Maybe it is just me but honestly I have no idea how to implement this: https://drift.simonbinder.eu/docs/advanced-features/isolates/
I'd like fully working examples where I can see in which files the given method is, how it is used throughout the app (is it stored somewhere or open a new appdatabase whenever needed), what is in the main etc...
Do I just randomly call into this db from main thread's method middle part and it will run next code on a new thread? Or I have to call the method in a new isolate to make it work?
I'd like to run some inserts in an isolate since it is blocking the UI but I can't decode what's in the doc. A complete project with examples would help a lot...