Agung
Agung

Reputation: 13813

how to save data in Hive database when receiving data in the background?

I have an issue saving data to Hive when receiving Firebase Cloud Messaging (FCM) push notification data when the app is in the background.

I have a static method to set up hive like this

static Future<void> setUpHive() async {
    try {

      await Hive.initFlutter();

      if (!Hive.isBoxOpen("Box Name")) {
          await Hive.openBox("Box Name");
      }

    } catch (error) {
      print(error.toString());
    }
}

I use that setUpHive static method in main function like this

Future<void> main() async {

  await HiveHelper.setUpHive();

  runApp(
    MyApp(),
  );
}

when the app is in the background, and then it receives FCM message, then this code below will be called. after that I try change the data stored in the Hive box

Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
    // when receive FCM message when app is in the background, this block will be executed
    
    // set up the hive first
    await HiveHelper.setUpHive(); 

    // then I try to change the data stored in the Hive box
    final myBox = Hive.box("BOX NAME");
    myBox.put("key", 12345);
}

it seems okay after receiving FCM background data, but when I fully close the app, and the main called again I have error when trying to open the box like this

   static Future<void> setUpHive() async {
        try {

          await Hive.initFlutter();

          if (!Hive.isBoxOpen("Box Name")) {
              await Hive.openBox("Box Name"); // Error in this line
          }

        } catch (error) {
          print(error.toString());
        }
    }

the error is:

HiveError: This should not happen. Please open an issue on GitHub. E/flutter (13142): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: HiveError: This should not happen. Please open an issue on GitHub. E/flutter (13142): #0 BinaryReaderImpl.readFrame (package:hive/src/binary/binary_reader_impl.dart:250:7) E/flutter

I try to find the solution, and I find similar issue from here about Using Hive DB in a Background Process and it is said

leisim:

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...

I am new in Flutter, and I don't understand what he said. please help :(

Upvotes: 2

Views: 3468

Answers (3)

Witoldio
Witoldio

Reputation: 951

Specially for that case was created package isolated_box. Box is already isolated and reusable and you still have possibility to stream changes.

Upvotes: 0

zorro
zorro

Reputation: 129

You can try the following code. The basic idea is to send data from background isolate to main isolate.

Future<void> backgroundMessageHandler(RemoteMessage msg){
  IsolateNameServer.lookupPortByName('main_port')?.send(msg);
}

@override
void initState(){
  super.initState();

  ReceivePort receivePort = ReceivePort();
  IsolateNameServer.registerPortWithName(receivePort.sendPort,'main_port');

  receivePort.listen((message) {
    if(message is RemoteMessage){
      //TODO: save your data in hive box
    }
  }
    
}

Upvotes: 3

Tolga Kartal
Tolga Kartal

Reputation: 633

You need to close your hive box in the main isolate once app goes into background. When it does, you need to CRUD in the background isolate. If you want to sync data between two isolates (because they don't share the same hive data) then you need a two way communication between isolates.

Here is an example code of communicating between two isolates.

import 'dart:io'; // for exit();
import 'dart:async';
import 'dart:isolate';

Future<SendPort> initIsolate() async {
  Completer completer = new Completer<SendPort>();
  ReceivePort isolateToMainStream = ReceivePort();

  isolateToMainStream.listen((data) {
    if (data is SendPort) {
      SendPort mainToIsolateStream = data;
      completer.complete(mainToIsolateStream);
    } else {
      print('[isolateToMainStream] $data');
    }
  });

  Isolate myIsolateInstance = await Isolate.spawn(myIsolate, isolateToMainStream.sendPort);
  return completer.future;
}

void myIsolate(SendPort isolateToMainStream) {
  ReceivePort mainToIsolateStream = ReceivePort();
  isolateToMainStream.send(mainToIsolateStream.sendPort);

  mainToIsolateStream.listen((data) {
    print('[mainToIsolateStream] $data');
    exit(0);
  });

  isolateToMainStream.send('This is from myIsolate()');
}

void main() async {
  SendPort mainToIsolateStream = await initIsolate();
  mainToIsolateStream.send('This is from main()');
}

for more go to https://medium.com/@lelandzach/dart-isolate-2-way-communication-89e75d973f34

Upvotes: 0

Related Questions