LeeDongHee
LeeDongHee

Reputation: 165

Flutter - Listening to data using workmanager

I am using Flutter, I want to receive notifications through mqtt communication even when the app is in the background state and the device screen is off using mqtt_client and workmanager package,

xcode -> debug -> background fetch The following message is exposed and mqtt cannot be listened to.

iOS background fetch

022-09-21 14:41:49.499500+0900 Runner[13669:4008622] [workmanager.BackgroundWorker] performBackgroundRequest(_:) -> performBackgroundRequest.failed (finished in 0.04 seconds)

If I use localNotification instead of initMQTT in Workmanager().executeTask it works fine.


main.dart

final MqttServerClient client = MqttServerClient(Env.WEBSOCKET_URL, '');

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    switch (task) {
      case Workmanager.iOSBackgroundTask:
        stderr.writeln("iOS background fetch");
        initMQTT(client);
        break;
    }

    stderr.writeln("iOS background complete");
    bool success = true;
    return Future.value(success);
  });
}

void initWorkManager(MqttServerClient client) async {
  Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
  Workmanager().registerOneOffTask(
    "task-identifier",
    "task-identifier", // Ignored on iOS
    initialDelay: const Duration(seconds: 10),
    constraints: Constraints(
      networkType: NetworkType.connected,
      requiresCharging: false,
    ), // fully supported
  );
}


void main() async {
  FlutterNativeSplash.preserve(
    widgetsBinding: WidgetsFlutterBinding.ensureInitialized(),
  );
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
  await dotenv.load(fileName: Env.fileName);
  await EasyLocalization.ensureInitialized();

  await Hive.initFlutter();
  Hive.registerAdapter<NoticeItemModel>(NoticeItemModelAdapter());
  await Hive.openBox<int>(HiveBoxConstant.NOTICE_UPDATE_TIME);
  await Hive.openBox<NoticeItemModel>(HiveBoxConstant.NOTICE_ITEM);
  await Hive.openBox<DateTime>(HiveBoxConstant.EVENT_BANNER_HIDE_TIME);


  initMQTT(client);


  Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
  Workmanager().registerOneOffTask("task-identifier", "simpleTask");



  MobileAds.instance.initialize();

  runApp(
    ProviderScope(
      child: EasyLocalization(
        supportedLocales: const [Locale('en'), Locale('ko')],
        path: 'assets/translations',
        fallbackLocale: const Locale('en'),
        child: const AppMain(),
      ),
    ),
  );
}

mqtt_init.dart


Future<void> initMQTT(MqttServerClient client) async {
  print('mqtt init');

  client.useWebSocket = true;
  client.port = 9001;
  client.logging(on: kDebugMode ? true : false);
  client.setProtocolV311();
  client.keepAlivePeriod = 60 * 15;
  client.connectTimeoutPeriod = 5000;
  client.autoReconnect = true;

  String id = '';
  DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
  if (Platform.isAndroid) {
    final AndroidDeviceInfo androidData = await deviceInfo.androidInfo;
    id = androidData.androidId ?? '';
  } else if (Platform.isIOS) {
    final IosDeviceInfo iosData = await deviceInfo.iosInfo;
    id = iosData.identifierForVendor ?? '';
  }
  final connMess = MqttConnectMessage()
      .withClientIdentifier('${id}_${Platform.localeName}')
      .authenticateAs(Env.WEBSOCKET_ID, Env.WEBSOCKET_PW)
      .withWillTopic('conntect')
      .withWillMessage('connect message')
      .startClean()
      .withWillQos(MqttQos.atLeastOnce);
  print('MQTT connecting....');
  client.connectionMessage = connMess;

  try {
    await client.connect();
  } on NoConnectionException catch (e) {
    print('MQTT exception - $e');
    client.disconnect();
  } on SocketException catch (e) {
    print('Socket exception - $e');
    client.disconnect();
  }

  if (client.connectionStatus!.state == MqttConnectionState.connected) {
    print('[MQTT connected]');
  } else {
    print('''
        [ERROR MQTT connection failed]
        disconnecting, status is ${client.connectionStatus}
        ''');
    client.disconnect();
    exit(-1);
  }
  final String userKey = await SecureStorageViewModel.getUserKey();
  client.subscribe(Env.WEBSOCKET_TOPIC_NOTI_ANNOUNCE, MqttQos.exactlyOnce);
  client.subscribe(Env.WEBSOCKET_TOPIC_NOTI_EVENT, MqttQos.exactlyOnce);
  client.subscribe(Env.WEBSOCKET_TOPIC_NOTI_NOTICE, MqttQos.exactlyOnce);
  client.subscribe(Env.WEBSOCKET_TOPIC_NOTI_SEASON, MqttQos.exactlyOnce);
  client.subscribe(Env.WEBSOCKET_TOPIC_NOTI_UPDATE, MqttQos.exactlyOnce);
  client.subscribe(
      '${Env.WEBSOCKET_TOPIC_NOTI_WALLET}$userKey', MqttQos.exactlyOnce);

  mqttListen(client);
}

void mqttListen(MqttServerClient client) {
  client.updates!.listen((List<MqttReceivedMessage<MqttMessage?>>? c) async {
    print('mqtt listen!');
    final recMess = c![0].payload as MqttPublishMessage;
    final topic = c[0].topic;
    final message =
        MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
    final String userKey = await SecureStorageViewModel.getUserKey();

    if (topic == Env.WEBSOCKET_TOPIC_NOTI_ANNOUNCE) {}
    if (topic == Env.WEBSOCKET_TOPIC_NOTI_EVENT) {}
    if (topic == Env.WEBSOCKET_TOPIC_NOTI_NOTICE) {}
    if (topic == Env.WEBSOCKET_TOPIC_NOTI_SEASON) {}
    if (topic == Env.WEBSOCKET_TOPIC_NOTI_UPDATE) {}
    if (topic == '${Env.WEBSOCKET_TOPIC_NOTI_WALLET}$userKey') {}

    LocalNotification.addNotification(
      type: NotificationConstants.notice,
      remainingSeconds: 5,
    );
  });
}

flutter doctor -v

[✓] Flutter (Channel stable, 3.3.2, on macOS 12.6 21G115 darwin-x64, locale ko-KR)
    • Flutter version 3.3.2 on channel stable at /Users/leedonghee/Documents/libraries.nosync/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision e3c29ec00c (7일 전), 2022-09-14 08:46:55 -0500
    • Engine revision a4ff2c53d8
    • Dart version 2.18.1
    • DevTools version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc3)
    • Android SDK at /Users/leedonghee/Library/Android/sdk
    • Platform android-32, build-tools 33.0.0-rc3
    • ANDROID_HOME = /Users/leedonghee/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.0)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14A309
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] VS Code (version 1.71.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.48.0

[✓] Connected device (3 available)
    • iPhone 14 Pro (mobile) • DC0EC2DD-FF96-417C-93A8-2843F8D8BF8E • ios            • com.apple.CoreSimulator.SimRuntime.iOS-16-0 (simulator)
    • macOS (desktop)        • macos                                • darwin-x64     • macOS 12.6 21G115 darwin-x64
    • Chrome (web)           • chrome                               • web-javascript • Google Chrome 105.0.5195.125

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

finally When you run the app for the first time, workmanager exposes the following log.

[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(bgTaskSchedulingFailed(Error Domain=BGTaskSchedulerErrorDomain Code=1 "(null)") error, Scheduling the task using BGTaskScheduler has failed.

This may be due to too many tasks being scheduled but not run.

See the error for details: Error Domain=BGTaskSchedulerErrorDomain Code=1 "(null)"., null, null)
#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:653:7)
#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:296:18)
<asynchronous suspension>
#2      Workmanager.registerOneOffTask (package:workmanager/src/workmanager.dart:187:7)
<asynchronous suspension>

Upvotes: 1

Views: 954

Answers (0)

Related Questions