easazade / android_long_task

android long task is a flutter plugin to run dart code in an android foreground service with simplicity
Apache License 2.0
16 stars 6 forks source link

Using flutter_blue together with android-long-task leads to a PlatformException #2

Closed ahoelzemann closed 2 years ago

ahoelzemann commented 3 years ago

Hey @easazade ,

I'm trying to use your package to download files from a BLE device in the background. It runs fine so far. However, I receive the following exception when I try to run any function from flutter_blue https://pub.dev/packages/flutter_blue

As soon as I try to run my initialization function for the BLEManager

 Future<bool> asyncInit() async {
    flutterBlue = FlutterBlue.instance;
    await flutterBlue.stopScan();
    for (var device in await flutterBlue.connectedDevices) {
      await device.disconnect();
    }
    return true;
  }

I get the following exception:

E/flutter (11319): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: MissingPluginException(No implementation found for method isAvailable on channel plugins.pauldemarco.com/flutter_blue/methods) E/flutter (11319): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:175:7) E/flutter (11319): E/flutter (11319): #.1 FlutterBlue._setLogLevelIfAvailable (package:flutter_blue/src/flutter_blue.dart:78:9) E/flutter (11319): E/flutter (11319):

Any idea what's going wrong?

best regards and thanks for the nice plugin!

2math commented 3 years ago

I have the same issue while testing the plugin on my side with Location plugin. At the same time I can make network calls and use the http plugin

serviceMain() async {
  WidgetsFlutterBinding.ensureInitialized();

  ServiceClient.setExecutionCallback((initialData) async {
    var serviceData = AppServiceData.fromJson(initialData);

    Log.w('initialData : $serviceData', "tag");

    var body = {
      'userId': "me",
      'password': "123",
    };

    Call call = Call.name(CallMethod.POST, 'v1/login', body: jsonEncode(body), refreshOn401: false);

    var serverData = await BaseNetworkManager().doServerCall(call, (json) {
      return jsonDecode(json);
    }).catchError((e) {
      Log.error("login", error: e);
    });

    Log.i('server data : $serverData', "tag");

    var locationData = await Location().hasPermission().catchError((e){
      Log.error("Location", error: e);
    });

    Log.i("my location $locationData");
  });
}

The crash log when I call Location().hasPermission()

E/MethodChannel#lyokone/location( 5972): Failed to handle method call
E/MethodChannel#lyokone/location( 5972): java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.lyokone.location.FlutterLocation.checkPermissions()' on a null object reference
E/MethodChannel#lyokone/location( 5972):    at com.lyokone.location.MethodCallHandlerImpl.onHasPermission(MethodCallHandlerImpl.java:123)
E/MethodChannel#lyokone/location( 5972):    at com.lyokone.location.MethodCallHandlerImpl.onMethodCall(MethodCallHandlerImpl.java:41)
E/MethodChannel#lyokone/location( 5972):    at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/MethodChannel#lyokone/location( 5972):    at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
E/MethodChannel#lyokone/location( 5972):    at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:738)
E/MethodChannel#lyokone/location( 5972):    at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#lyokone/location( 5972):    at android.os.MessageQueue.next(MessageQueue.java:335)
E/MethodChannel#lyokone/location( 5972):    at android.os.Looper.loop(Looper.java:183)
E/MethodChannel#lyokone/location( 5972):    at android.app.ActivityThread.main(ActivityThread.java:7660)
E/MethodChannel#lyokone/location( 5972):    at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#lyokone/location( 5972):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
E/MethodChannel#lyokone/location( 5972):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

I think such a plugins needs to be registered again for the foreground service, because probably runs in another isolate. Here is what the guys from flt_worker has to let the devs to register extra plugins in the background isolate https://github.com/xinthink/flt_worker/blob/4e93edfe02e3d1eba05230233d29d25ead2bd085/android/src/main/java/dev/thinkng/flt_worker/internal/BackgroundWorkerPlugin.java#L100

Then in my activity I can register such a plugins :

public class MainActivity extends FlutterFragmentActivity {
    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);

        // set a callback to register all plugins to a headless engine instance
        FltWorkerPlugin.registerPluginsForWorkers = registry -> {
            io.flutter.plugins.pathprovider.PathProviderPlugin.registerWith(
                    registry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));

            io.flutter.plugins.connectivity.ConnectivityPlugin.registerWith(registry.registrarFor("io.flutter.plugins.connectivity.ConnectivityPlugin"));

            io.flutter.plugins.deviceinfo.DeviceInfoPlugin.registerWith(registry.registrarFor("io.flutter.plugins.deviceinfo.DeviceInfoPlugin"));

            com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.registerWith(registry.registrarFor("com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin"));

            io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin.registerWith(registry.registrarFor("io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin"));

            com.tekartik.sqflite.SqflitePlugin.registerWith(registry.registrarFor("com.tekartik.sqflite.SqflitePlugin"));

            return null;
        };
    }
}

On flutter_workmanager they have even a way that auto register all plugins on android https://github.com/fluttercommunity/flutter_workmanager/blob/master/android/src/main/kotlin/be/tramckrijte/workmanager/BackgroundWorker.kt#L80

2math commented 3 years ago

I have tried to manually register location package from the foreground service, but this is not the case. I can see a log message that this plugin is already registered for this FlutterEngine, so my comment above is invalid and you don't need to register any external plugins. However the problem with location plugin is in the plugin it self, which require an activity to work.

@easazade , probably this is the problem with your plugin too?

2math commented 3 years ago

With https://github.com/Baseflow/flutter-geolocator I can get locations and everything works great. So I confirm the problem is with Location plugin, which apparently needs an activity to work

easazade commented 2 years ago

accessing location on background and foreground is different. and I mean natively different and you should use a different library for that