Reputation: 165
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