Hive: Using Hive DB in a Background Process

Created on 11 Nov 2019  路  14Comments  路  Source: hivedb/hive

Question
I am trying to run a background process in Flutter where the contents of my Hive box are updated periodically. I understand that I've to most probably use an Isolate to achieve background dart execution, but I'm unsure how to go about it.

Can anybody help me out here, probably with a small sample? I am new to Isolates, and quite confused as to how to approach this.

Version

  • Platform: iOS, Android
  • Flutter version: 1.10.15
  • Hive version: 1.1.1
question

Most helpful comment

Unfortunately, Hive does not support opening boxes in multiple isolates. That means you can either close the box in the main isolate, update it in your background isolate and reopen it in the main isolate or you pass the data from the background to the main isolate and perform the update there...

All 14 comments

Unfortunately, Hive does not support opening boxes in multiple isolates. That means you can either close the box in the main isolate, update it in your background isolate and reopen it in the main isolate or you pass the data from the background to the main isolate and perform the update there...

So yes, I will only be executing from this isolate when the app is closed, from the background service. I hope that will resolve the concurrency issue.

Then it is no problem. You cannot do anything wrong. Hive checks if a box is opened in another isolate and throws an exception which tells you what is going on...

Could you please help me with a small sample of how I can do this on an Isolate? How I would write and execute such an isolate in the first place?

Creating an Isolate is simple in Flutter, there's a compute function that automatically creates one. Otherwise, you'd need to manually spawn an Isolate via Isolate.spawn. It's possible to have Dart code execute in the background while the app isn't running, but it's not very well documented yet. Here's the only piece about background execution on the Flutter website, which just points to a Medium article with sample code.

Thanks @marcelgarus, I've already had a look at these resources. Still having a bit of a tough time figuring out how to get it to work. The medium article is not beginner friendly. What I understand is that the background processes need to be set up using native code which will be called from flutter. Now, from that native code that gets executed, I need to call flutter/dart code (which will be in an isolate).

I tried implementing this with compute(), and here is the code that i've written:

Future<void> executeBgUpdate() async {
  return compute(_performUpdate, null);
}

Future<void> _performUpdate(_) async {

  WidgetsFlutterBinding.ensureInitialized();
  final appDocumentDir =
  await path_provider.getApplicationDocumentsDirectory();
  Hive.init(appDocumentDir.path);

  final _productList = await Hive.openBox(ProductManager.boxName);

  List<Future<Product>> futures = List<Future<Product>>();

  for (var product in _productList.values) {
    Future<Product> productFuture = ProductProvider().refreshProduct(product);
    futures.add(productFuture);
    productFuture.then((result) async =>
    await _productList.put(result.hashCode, result));
  }

  return Future.wait(futures);
}

I started without the WidgetsFlutterBinding.ensureInitialized();, but I caught an exception asking me to add this, and I added this, but now I am getting an error on this:

E/flutter (14862): [ERROR:flutter/lib/ui/ui_dart_state.cc(144)] Unhandled Exception: Exception: error: native function 'Window_setNeedsReportTimings' (2 arguments) cannot be found
E/flutter (14862): #0      Window.onReportTimings= (dart:ui/window.dart:955:7)
E/flutter (14862): #1      SchedulerBinding.addTimingsCallback (package:flutter/src/scheduler/binding.dart:233:14)
E/flutter (14862): #2      SchedulerBinding.initInstances (package:flutter/src/scheduler/binding.dart:206:7)
E/flutter (14862): #3      PaintingBinding.initInstances (package:flutter/src/painting/binding.dart:21:11)
E/flutter (14862): #4      SemanticsBinding.initInstances (package:flutter/src/semantics/binding.dart:22:11)
E/flutter (14862): #5      RendererBinding.initInstances (package:flutter/src/rendering/binding.dart:29:11)
E/flutter (14862): #6      WidgetsBinding.initInstances (package:flutter/src/widgets/binding.dart:253:11)
E/flutter (14862): #7      new BindingBase (package:flutter/src/foundation/binding.dart:56:5)
E/flutter (14862): #8      new _WidgetsFlutterBinding&BindingBase&GestureBinding (package:flutter/src/widgets/binding.dart)
E/flutter (14862): #9      new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding (package:flutter/src/widgets/binding.dart)
E/flutter (14862): #10     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding (package:flutter/src/widgets/binding.dart)
E/flutter (14862): #11     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding (package:flutter/src/widgets/binding.dart)
E/flutter (14862): #12     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding (package:flutter/src/widgets/binding.dart)
E/flutter (14862): #13     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding (package:flutter/src/widgets/binding.dart)
E/flutter (14862): #14     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding (package:flutter/src/widgets/binding.dart)
E/flutter (14862): #15     new WidgetsFlutterBinding (package:flutter/src/widgets/binding.dart)
E/flutter (14862): #16     WidgetsFlutterBinding.ensureInitialized (package:flutter/src/widgets/binding.dart:1083:7)
E/flutter (14862): #17     _performUpdate (package:price_tracker/services/background_update_service.dart:17:25)
E/flutter (14862): #18     _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter (14862): #19     _performUpdate (package:price_tracker/services/background_update_service.dart:15:28)
E/flutter (14862): #20     _IsolateConfiguration.apply (package:flutter/src/foundation/_isolates_io.dart:77:16)
E/flutter (14862): #21     _spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:84:45)
E/flutter (14862): #22     _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter (14862): #23     _spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:84:5)
E/flutter (14862): #24     Timeline.timeSync (dart:developer/timeline.dart:161:22)
E/flutter (14862): #25     _spawn (package:flutter/src/foundation/_isolates_io.dart:82:18)
E/flutter (14862): #26     _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter (14862): #27     _spawn (package:flutter/src/foundation/_isolates_io.dart:80:26)
E/flutter (14862): #28     _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:308:17)
E/flutter (14862): #29     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12)

I have no idea why this error happens but it has probably nothing to do with Hive.

I would try to remove code until it works to see what causes this error...

Yes @leisim, the code hasn't reached the Hive part. So, in order to initialize Hive, I'm using path_provider, which in turn requires the app to be running (and hence we're using WidgetsFlutterBinding.ensureInitialized if we execute Hive.init() before our app starts.

But the isolate is a thread with completely independent memory, so we have to initialize a session in the isolate for Hive. But we don't have the Flutter session running for the isolate, and running await path_provider.getApplicationDocumentsDirectory(); isn't working, even with WidgetsFlutterBinding.ensureInitialized. How do I solve this issue, considering I don't want the box placed anywhere outside of the app's respective document directory?

Maybe you could call getApplicationDocumentsDirectory() in your main isolate and pass it to the performUpdate() isolate.

Yes, I did that and it is working now!

Sorry for bringing the issue up again. Firebase Messaging onBackground handler executes code in a background isolate link. I called Hive.close() to close to Hive when app enters in the background mode so that i can call it from the onBackground Isolate. I can receive a notification and store but on app resume when i read from hive i still get my old stale data until i restart the application. Please help me.

Sorry for bringing the issue up again. Firebase Messaging onBackground handler executes code in a background isolate link. I called Hive.close() to close to Hive when app enters in the background mode so that i can call it from the onBackground Isolate. I can receive a notification and store but on app resume when i read from hive i still get my old stale data until i restart the application. Please help me.

Because dart isolates doesn't shares memory, hive instance on UI isolate doesn't notice the change when you do something on background isolate. So you might have to somehow notify your UI isolate to reload data (by closing / reopening boxes).

Because dart isolates doesn't shares memory, hive instance on UI isolate doesn't notice the change when you do something on background isolate. So you might have to somehow notify your UI isolate to reload data (by closing / reopening boxes).

I have a code implemented a code that closes all the boxes upon app going to Background mode and Reopen them when app is in Foreground. Yet still the data can't sync. Can you please help me with a code solving it or direct me to an article.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yannickvg picture yannickvg  路  4Comments

MyoLinOo picture MyoLinOo  路  3Comments

abacaj picture abacaj  路  3Comments

kaboc picture kaboc  路  3Comments

ProfileID picture ProfileID  路  4Comments