hive
hive copied to clipboard
How to run Hive in an isolate?
Question Hi, I'm new to Hive and I'm trying to integrate it into my project. I've managed to generate a large static database that performs really well (much better than sqlite), and it works perfectly when I'm running the part of the project that uses it alone outside of flutter. However, this part runs in an isolate because otherwise it freezes the UI, and the database has to be inside this part because otherwise the structure of the code would be horrific and unmaintainable, and this presents a problem.
When I try to run hive in the isolate I run into this error:
E/flutter (17531): [ERROR:flutter/runtime/dart_isolate.cc(865)] Unhandled exception:
E/flutter (17531): ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
E/flutter (17531): If you're running an application and need to access the binary messenger before `runApp()` has been called (for example, during plugin initialization), then you need to explicitly call the `WidgetsFlutterBinding.ensureInitialized()` first.
E/flutter (17531): If you're running a test, you can call the `TestWidgetsFlutterBinding.ensureInitialized()` as the first line in your test's `main()` method to initialize the binding.
Of course I have WidgetsFlutterBinding.ensureInitialized() in main() before runApp(), and I cannot put it in the isolate because I get this:
E/flutter (17181): UI actions are only available on root isolate.
So far I've tried:
- Hive.init() inside the isolate
- Hive.init() in main
- Hive.initFlutter() in the isolate (can't do UI things outside the main isolate)
- Hive.initFlutter() in main
I get the same error whatever I do. I've seen some people here talking about running Hive in an isolate here so I know it's possible, I just don't know what I'm missing here.
Version
- Platform: Android / iOS
- Flutter version: 1.20.2
- Hive version: 1.4.4
Thanks in advance
Hm interesting. Hive.init() (not initFlutter) should be worked correctly because it doesn't uses flutter specific features.
Could you write simple reproducible example for "Hive.init() inside the isolate"?
You are probably using path_provider which relies on platform channels. They are probably not initialized yet when you access the path.
Edit: You should get the path in the main isolate and send it to the isolate where you try to open a Hive instance.
You are probably using
path_providerwhich relies on platform channels. They are probably not initialized yet when you access the path.Edit: You should get the path in the main isolate and send it to the isolate where you try to open a Hive instance.
Actually this seems to be the solution. I wasn't using path_provider - it didn't occur to me that the path wouldn't be resolved automatically in the non-flutter dart plugin.
That being said, I now have a different problem in that I don't know what path I'm supposed to give it and none of the obvious ones seem to work.
Am I supposed to include /lib? At the moment I have a 'db' folder in lib with the hive database in it and I'm trying to access it like:
final appDocDir = await getApplicationDocumentsDirectory();
String dbPath = appDocDir.path + '/lib/db';
I've also tried appDocDir.path + '/db' and that doesn't work either. The folder is listed as an asset in pubspec.yaml
I've made a quick minimal reproducible example here if either if you wouldn't mind pointing me in the right direction: https://github.com/alexobviously/hive_in_isolate
@alexobviously,
I don't know what you're trying to accomplish, but something is wrong. You're trying to access asset from filesystem. Flutter assets are bundled inside executable files which means those files (assets) will not be available from file system.
@alexobviously,
I don't know what you're trying to accomplish, but something is wrong. You're trying to access asset from filesystem. Flutter assets are bundled inside executable files which means those files (assets) will not be available from file system.
I'm trying to include a pre-generated hive database (i.e. users never change it, it's static) with the app. How can I access it then?
@alexobviously, I don't know what you're trying to accomplish, but something is wrong. You're trying to access asset from filesystem. Flutter assets are bundled inside executable files which means those files (assets) will not be available from file system.
I'm trying to include a pre-generated hive database (i.e. users never change it, it's static) with the app. How can I access it then?
I'm not sure but I think the code below should work:
Future<Box<T>> openBoxFromAsset<T>(String boxName, String assetName) async {
var data = await rootBundle.load(assetName);
var bytes = data.buffer.asUint8List();
return Hive.openBox<T>(boxName, bytes: bytes);
}
So, you can use var box = await openBoxFromAsset('test', 'assets/test.hive') to open box file.
Please note that I haven't tested the code.
I also suggest placing assets (hive db for-example) in a folder named assets in project root directory (not inside lib).
@alexobviously, I don't know what you're trying to accomplish, but something is wrong. You're trying to access asset from filesystem. Flutter assets are bundled inside executable files which means those files (assets) will not be available from file system.
I'm trying to include a pre-generated hive database (i.e. users never change it, it's static) with the app. How can I access it then?
I'm not sure but I think the code below should work:
Future<Box<T>> openBoxFromAsset<T>(String boxName, String assetName) async { var data = await rootBundle.load(assetName); var bytes = data.buffer.asUint8List(); return Hive.openBox<T>(boxName, bytes: bytes); }So, you can use
var box = await openBoxFromAsset('test', 'assets/test.hive')to open box file.Please note that I haven't tested the code.
I also suggest placing assets (hive db for-example) in a folder named assets in project root directory (not inside
lib).
Thanks very much, this actually works perfectly outside the isolate. However, inside the isolate I'm still running into the ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized. error. I assume this is because rootBundle is a flutter thing, but I'm not sure how to get around this since isolates can't access flutter things as far as I'm aware?
Maybe I can load the database in the main isolate and just pass the Box object to the isolate? Is that possible, and how do you think that would perform with a 50MB+ database?
Maybe I can load the database in the main isolate and just pass the Box object to the isolate? Is that possible, and how do you think that would perform with a 50MB+ database?
I would copy the database to the filesystem, pass the path to the isolate and open the database there.
Is it still the best solution? I have a quite big dynamic database that is very slow to initialise, even when I use lazy boxes for parts of it. I was thinking if putting it to isolate can improve performance a bit and at least allow me to show loading indicator on UI thread.
Any news on this? I also have a very database that is very slow to initialize. I still haven't had much luck with using isolates in a nice way for loading Hive databases.