Send platform message to Android method from background flutter isolate - android

I want to have a dart background service running forever (isolates) which will communicate with a server through websockets. And I have an API for Android which gather information to send to the server.
How can I invoke that Android methods which use callbacks and everything from the background isolate?
EDIT
So far in dart I created an Isolate to periodically invoke the poolSong method in background even if the user is using another app or has the screen turned off.
But that gives me the error below...on the github issues they say that I can't send a platform message from a different Isolate unless it's the main. But if I do it from the main isolate, when the user exit the app that isolate will terminate too.
MainDart
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
static const platform = const MethodChannel('mainService');
static _poolSong(SendPort sendPort) async {
const oneSec = const Duration(seconds:1);
new Timer.periodic(oneSec, (Timer t) => platform.invokeMethod('poolSong'));
}
#override
void initState() {
super.initState();
ReceivePort receivePort = ReceivePort();
Isolate.spawn(_poolSong, receivePort.sendPort);
}
·
·
·
MainActivityJava
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
(call, result) -> {
if (call.method.equals("startService"))
startService();
if (call.method.equals("poolSong"))
poolSong();
}
);
}
ERROR
E/flutter (25412): [ERROR:flutter/runtime/dart_isolate.cc(717)] Isolate (765499726) 'main.dart:_poolSong()' exited with an error
E/flutter (25412): [ERROR:flutter/shell/common/shell.cc(186)] Dart Error: Unhandled exception:
E/flutter (25412): error: native function 'Window_sendPlatformMessage' (4 arguments) cannot be found
E/flutter (25412): #0 Window.sendPlatformMessage (dart:ui/window.dart:811:9)
E/flutter (25412): #1 BinaryMessages._sendPlatformMessage (package:flutter/src/services/platform_messages.dart:39:15)
E/flutter (25412): #2 BinaryMessages.send (package:flutter/src/services/platform_messages.dart:87:12)
E/flutter (25412): #3 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:286:49)
E/flutter (25412): <asynchronous suspension>
E/flutter (25412): #4 _MyHomePageState._poolSong.<anonymous closure> (package:musictie/main.dart:37:54)
E/flutter (25412): #5 _Timer._runTimers (dart:isolate/runtime/libtimer_impl.dart:382:19)
E/flutter (25412): #6 _Timer._handleMessage (dart:isolate/runtime/libtimer_impl.dart:416:5)
E/flutter (25412): #7 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

This error happens when trying to run dart code when the app is in the garbage (app closed), Until now the best way for dealing with background service is to do what you need in native code, and dont try to run anything from dart when app closed.

Since Flutter 3.7 plugins and platform channels can be used from any isolate, not only from the root one.
References:
https://medium.com/flutter/introducing-background-isolate-channels-7a299609cad8
https://github.com/flutter/flutter/issues/13937

Related

Permissions in flutter background_fetch headless task

I've got an Android app written in Dart using the Flutter framework. When it's first opened up, it requests permission to access location in the background. When the user opens the app using the launcher icon, this all works fine.
I use the background_fetch package to configure a headless task that runs on boot. This works, but it appears the context this runs in doesn't have the same permissions as the app. I check the permissions like this:
status = await Permission.location.status;
locationGranted = status.isGranted;
status = await Permission.locationAlways.status;
locationAlwaysGranted = status.isGranted;
if (locationAlwaysGranted) {
...
}
If I run this code from the app's main() then the body of the if statement is executed; when it runs from the headless task after the phone restarts, the body of the if block is not executed.
Is there a way to get the headless task to have the same permissions as the app? Or is there a better way to restart background location tracking after a phone restart?
The background-fetch task is configured like this:
BackgroundFetch.registerHeadlessTask(headlessTask);
BackgroundFetch.configure(BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
startOnBoot: true,
requiresBatteryNotLow: false,
requiresStorageNotLow: false,
requiresCharging: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.NONE
), (String taskId) {
...
BackgroundFetch.finish(taskId);
});
BackgroundFetch.scheduleTask(TaskConfig(
delay: 0,
periodic: false,
startOnBoot: true,
taskId: "gfz_on_boot",
enableHeadless: true
));
Edited to add this is the stack trace that appears in the flutter logs:
E/flutter ( 6464): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: PlatformException(PermissionHandler.PermissionManager, Unable to detect current Android Activity., null, null)
E/flutter ( 6464): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter ( 6464): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:167:18)
E/flutter ( 6464): <asynchronous suspension>
E/flutter ( 6464): #2 MethodChannelPermissionHandler.requestPermissions (package:permission_handler_platform_interface/src/method_channel/method_channel_permission_handler.dart:71:9)
E/flutter ( 6464): <asynchronous suspension>
E/flutter ( 6464): #3 GFZModel.updatePermissions (package:gfz_app/model.dart:349:24)
E/flutter ( 6464): <asynchronous suspension>
This makes sense at one level - there is no current activity in the background task - but how can we use permissions in background tasks?

Flutter ble writing fails

Flutter ble - write characteristic error
I'm not being able to write on ble pheriperal device, using flutter_blue library, it throws this exception:
Unhandled Exception: PlatformException(write_characteristic_error, service (0000ff06-0000-1000-8000-00805f9b34fb) could not be located on the device, null, null)
E/flutter (29808): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter (29808): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:167:18)
E/flutter (29808): <asynchronous suspension>
E/flutter (29808): #2 BluetoothCharacteristic.write (package:flutter_blue/src/bluetooth_characteristic.dart:120:18)
E/flutter (29808): <asynchronous suspension>
E/flutter (29808): #3 DeviceScreen._buildServiceTiles.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:untitled/main.dart:176:23)
or another error saying : Write is not permited.
I'm using example code provided by flutter blue library, here is the code that im trying to write after device is connected:
onWritePressed: () async {
await c.write(_getRandomBytes(), withoutResponse: true);
await c.read();
},
onNotificationPressed: () async {
await c.setNotifyValue(!c.isNotifying);
if (c.properties.read) {
await c.read();
}
List<int> _getRandomBytes(){
String command = "55 AA 00 01 50 3C 00";
return command.codeUnits;
}
The hardware device is working fine in Kotlin, data is sent and received.

Dart changing a variable value using own extension method

I was trying to delete elements of a list using my own extension but I got an error. This error says the extension is only readable.
This is my extension
extension ToListExtention on List {
List toDatatable() {
List columns = [];
List rows = [];
(this.first as Map).forEach((key, value) {columns.add(key);});
this.forEach((element) {
rows.add((element as Map).values);
});
**this.clear();**
return [columns,rows];
}
}
and this is the error that I got
E/flutter (24973): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Unsupported operation: read-only
E/flutter (24973): #0 QueryResultSet.length= (package:sqflite_common/src/collection_utils.dart:109:5)
E/flutter (24973): #1 ListMixin.clear (dart:collection/list.dart:342:10)
E/flutter (24973): #2 ToListExtention.toDatatable (package:dbdeneme/Extentions.dart:9:10)
E/flutter (24973): #3 _AnasayfaState.build.<anonymous closure>.<anonymous closure> (package:dbdeneme/main.dart:58:27)
E/flutter (24973): #4 _rootRunUnary (dart:async/zone.dart:1362:47)
E/flutter (24973): #5 _CustomZone.runUnary (dart:async/zone.dart:1265:19)
E/flutter (24973): <asynchronous suspension>
So, as you understand, the bolded line doesn't work. How can I do it inside my own extension?
In extensions, you cannot change the original value of the object.
You can copy the list and then do all kinds of manipulation on that list. But you cannot change the list.
Your problem is that the list you are calling toDatatable on is a QueryResultSet which implementation can be found here:
https://github.com/tekartik/sqflite/blob/81c0c30aa59d7d3f229bf6ce0494458f96f893c2/sqflite_common/lib/src/collection_utils.dart#L74
Which implements length (called by clear()) with the following:
#override
set length(int newLength) {
throw UnsupportedError('read-only');
}
So your current extension method are not going to work on this kind of List (List are just an interface in this case and QueryResultSet are implementing that interface).

Flutter Android Release Build CRASH - FirebaseCore Initialize App Issue

Currently creating an app with Flutter.
I have created an issue on the official github but figured I'd ask here too if thats cool.
Steps To Reproduct:
flutter run --release
Install on Android device (reproduced on multiple)
App will hang when attempting Firebase.initializeApp()
NOTE
I do not personally think this is a firebase issue but when we all had to update our app for AndroidX compatibility and firebase_message also called for us to create the Application.kt file to implement it's newest pubs (which I need to use due to other dependencies).
ANY HELP WOULD BE GREATLY APPRECIATED. I really think this has something to do with the Kotlin files. I say that BECAUSE I MOVED THE AWAIT FIREBASE>INITAPP call in the main.dart file lower, and I got the same kind of output but instead referencing the SharedPrefs pub I was using.
Make sense?
What do YOUR Kotlin files look like?
Main.Dart File:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await SharedPreferences.getInstance();
Admob.initialize();
await Admob.requestTrackingAuthorization();
DynamicLinkService.handleDynamicLinks();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]).then(
(_) {
runApp(
StringConstants(
child: AuthProvider(auth: Auth(), child: MyApp()),
),
);
},
);
}
pubspec.yaml
version: 1.0.0+29
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
firebase_core: "^0.5.3"
# Newly reworked plugins covered by this migration guide:
firebase_auth: "^0.18.4"
cloud_firestore: "^0.14.4"
cloud_functions: "^0.7.2"
firebase_storage: "^5.2.0"
firebase_messaging: ^7.0.3 # Updated to work with new core only plugins (no new changes):
firebase_admob: "^0.10.3"
firebase_analytics: "^6.3.0"
firebase_dynamic_links: "^0.6.3"
Output:
E/flutter (27399): [ERROR:flutter/lib/ui/ui_dart_state.cc(184)] Unhandled Exception: MissingPluginException(No implementation found for method Firebase#initializeCore on channel plugins.flutter.io/firebase_core)
E/flutter (27399): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:160)
E/flutter (27399): <asynchronous suspension>
E/flutter (27399): #1 MethodChannel.invokeListMethod (package:flutter/src/services/platform_channel.dart:348)
E/flutter (27399): <asynchronous suspension>
E/flutter (27399): #2 MethodChannelFirebase._initializeCore (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:30)
E/flutter (27399): <asynchronous suspension>
E/flutter (27399): #3 MethodChannelFirebase.initializeApp (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:75)
E/flutter (27399): <asynchronous suspension>
E/flutter (27399): #4 Firebase.initializeApp (package:firebase_core/src/firebase.dart:43)
E/flutter (27399): <asynchronous suspension>
E/flutter (27399): #5 main (package:instapray/main.dart:20)
E/flutter (27399): <asynchronous suspension>
E/flutter (27399):
MainActivity.kt:
class MainActivity: FlutterActivity() {
}
Application.kt:
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
class Application:FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingService.setPluginRegistrant(this)
}
override fun registerWith(registry:PluginRegistry) {
io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
}
This involved an update to the core Firebase pubs. Thanks.

Flutter - Keep app running in the background even when app is closed with bluetooth service

My project involves connecting to a Bluetooth device and reading constant data from the device.
I am using the plugin FlutterBleLib to connect to the Bluetooth device. The process of connecting to the device and reading data is working fine.
The issue I am having is when the user closes the app.
Found the following article: The Tricky Task of Keeping Flutter Running, which allows us to run the code in the background even after the app is closed. The sample runs a counter service in the background. My idea was to replace the counter service with the Bluetooth service.
The sample code was built on pre 1.12 Android project. So, I upgraded the code to embedding version 2 using the following resource.
I was able to build the project and everything is working fine on the main UI, but as soon as the app is closed, the code is starting the FlutterNativeView and then when it is trying to start the bluetooth service it is failing with the following error:
I/flutter: ══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
The following MissingPluginException was thrown while activating platform stream on channel
flutter_ble_lib/stateRestoreEvents:
MissingPluginException(No implementation found for method listen on channel
I/flutter: flutter_ble_lib/stateRestoreEvents)
When the exception was thrown, this was the stack:
#0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:159:7)
<asynchronous suspension>
#1 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:334:12)
#2 EventChannel.receiveBroadcastStream.<anonymous closure> (package:flutter/src/services/platform_channel.dart:542:29)
#3 EventChannel.receiveBroadcastStream.<anonymous closure> (package:flutter/src/services/platform_channel.dart:528:64)
#16 FlutterBleLib.restoredState (package:flutter_ble_lib/src/bridge/lib_core.dart:52:8)
#17 InternalBleManager.createClient (package:flutter_ble_lib/src/internal_ble_manager.dart:23:15)
#18 DevicesBloc.init (package:in_field/modules/eld_ble/devices_bloc.dart:54:10)
#19 BluetoothService.startBluetoothService (package:in_field/modules/eld_ble/background/bluetooth_service.dart:60:18)
#20 backgroundMain (package:in_field/modules/eld_ble/background/background_main.dart:11:22)
#21 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:233:25)
#26 _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:225:5)
#27 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
(elided 17 frames from class _RawReceivePortImpl and dart:async)
════════════════════════════════════════════════════════════════════════════════════════════════════
E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: MissingPluginException(No implementation found for method getState on channel flutter_ble_lib)
#0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:159:7)
<asynchronous suspension>
#1 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:334:12)
#2 BluetoothStateMixin.state (package:flutter_ble_lib/src/bridge/bluetooth_state_mixin.dart:29:8)
#3 BluetoothStateMixin.observeBluetoothState (package:flutter_ble_lib/src/bridge/bluetooth_state_mixin.dart:34:43)
<asynchronous suspension>
The issue is happening as soon the FlutterNativeView had been started.
Here is the code from the sample before the upgrade (BackgroundService.kt):
private fun startFlutterNativeView() {
Log.i("BackgroundService", "Starting FlutterNativeView")
FlutterMain.ensureInitializationComplete(this, null)
getCallbackInformation()?.let { flutterCallbackInformation ->
flutterNativeView = FlutterNativeView(this, true).apply {
GeneratedPluginRegistrant.registerWith(pluginRegistry)
val args = FlutterRunArguments().apply {
bundlePath = FlutterMain.findAppBundlePath()
entrypoint = flutterCallbackInformation.callbackName
libraryPath = flutterCallbackInformation.callbackLibraryPath
}
runFromBundle(args)
}
}
}
Here is the code after the upgrade:
private fun startFlutterNativeView() {
Log.i("BackgroundService", "Starting FlutterNativeView")
FlutterMain.ensureInitializationComplete(this, null)
getCallbackInformation()?.let { flutterCallbackInformation ->
flutterNativeView = FlutterNativeView(this, true).apply {
//GeneratedPluginRegistrant.registerWith(FlutterEngineCache.getInstance().get("test")!!)
//GeneratedPluginRegistrant.registerWith("com.polidea.flutter_ble_lib.FlutterBleLibPlugin");
//FlutterBleLibPlugin.registerWith(registry?.registrarFor("com.polidea.flutter_ble_lib.FlutterBleLibPlugin"))
//FlutterBleLibPlugin.registerWith(registrarFor("com.polidea.flutter_ble_lib.FlutterBleLibPlugin"))
//GeneratedPluginRegistrant.registerWith(FlutterEngine(applicationContext))
val args = FlutterRunArguments().apply {
bundlePath = FlutterMain.findAppBundlePath()
entrypoint = flutterCallbackInformation.callbackName
libraryPath = flutterCallbackInformation.callbackLibraryPath
}
runFromBundle(args)
}
}
}
The commented lines are the place where I am trying to register the Bluetooth plugin but it is failing with errors for all the above trials.
Can anyone tell me where I am doing wrong? Any help is greatly appreciated.
See float button overlay
Register your background service above the main method,
and call registerCallback() inside the main method.

Categories

Resources