web-dev-qa-db-fra.com

Comment faire en sorte que le plugin Flutter Workmanager et le plugin Location fonctionnent ensemble

1. Description du problème

Mon objectif est de créer une application Flutter qui obtient des mises à jour de localisation périodiques en utilisant ce plugin Workmanager et en utilisant ce plugin de localisation . Mais je ne peux pas charger correctement le plugin Location lorsque mon rappel Workmanager se déclenche. J'obtiens cette erreur:

MissingPluginException(No implementation found for method getLocation on channel lyokone/location)

Donc, fondamentalement, le problème est que lorsque le plugin Workmanager essaie d'exécuter du code Dart, il ne charge pas le plugin Location.

2. Autres ressources que j'ai recherchées

J'ai trouvé d'autres confrontés au même problème, ici , ici et ici .

Autant que je sache, la solution apportée à ces questions se résume à: créer un fichier nommé CustomApplication.Java, qui étend FlutterApplication, et qui enregistre votre plugin (s). Et puis enregistrez le fichier CustomApplication.Java dans votre fichier AndoidManifest.xml.

3. Mon code jusqu'à présent

J'ai essayé de créer une application minimale qui implémente les fonctionnalités dont j'ai besoin:

  1. J'ai implémenté le plugin Workmanager (fonctionne bien)
  2. J'ai implémenté le plugin Location (fonctionne bien)
  3. J'ai essayé de combiner ces fonctionnalités (ne fonctionne pas)

Pour voir exactement ce que j'ai fait à chaque étape, veuillez regarder ici: https://gitlab.com/tomoerlemans/workmanager_with_location/-/commits/master . (Ce référentiel peut également être utilisé pour répliquer rapidement le problème).

Les fichiers de code pertinents sont les suivants:

main.Dart

import 'package:flutter/material.Dart';
import 'package:workmanager/workmanager.Dart';
import 'package:location/location.Dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Workmanager.initialize(callbackDispatcher, isInDebugMode: true);
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              onPressed: () {
                Workmanager.registerPeriodicTask(
                    "1", "simpleTask");
              },
              child: Text("Start workmanager"),
            ),
            RaisedButton(
              onPressed: () {
                getLocation();
              },
              child: Text("Get current location"),
            ),
          ],
        ),
      ),
    );
  }
}

void callbackDispatcher() {
  Workmanager.executeTask((task, inputData) {
    print("Native called background task at ${DateTime.now().toString()}");
    getLocation();
    return Future.value(true);
  });
}

void getLocation() async {
  LocationData currentLocation;
  var location = new Location();
  try {
    currentLocation = await location.getLocation();
  } on Exception catch (e) {
    print("Error obtaining location: $e");
    currentLocation = null;
  }
  print("Location altitude: ${currentLocation.altitude}");
  print("Location longitude: ${currentLocation.longitude}");
}

pubspec.yaml

name: background_location
description: A new Flutter project.

version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2
  workmanager: ^0.2.0
  location: ^2.3.5


dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

Application personnalisée.Java

package io.flutter.plugins;

import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
import be.tramckrijte.workmanager.WorkmanagerPlugin;
import com.lyokone.location.LocationPlugin;

public class CustomApplication extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback {
    @Override
    public void onCreate() {
        super.onCreate();
        WorkmanagerPlugin.setPluginRegistrantCallback(this);

    }
    @Override
    public void registerWith(PluginRegistry registry) {
        WorkmanagerPlugin.registerWith(registry.registrarFor("be.tramckrijte.workmanager.WorkmanagerPlugin"));
        LocationPlugin.registerWith(registry.registrarFor("com.lyokone.location.LocationPlugin"));
    }
}

AndroidManifest.xml

<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
    package="com.example.background_location">        
    <uses-permission Android:name="Android.permission.ACCESS_FINE_LOCATION" />

    <application
        Android:name="io.flutter.plugins.CustomApplication"
        Android:label="background_location"
        Android:icon="@mipmap/ic_launcher">
        <activity
            Android:name=".MainActivity"
            Android:launchMode="singleTop"
            Android:theme="@style/LaunchTheme"
            Android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            Android:hardwareAccelerated="true"
            Android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action Android:name="Android.intent.action.MAIN"/>
                <category Android:name="Android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <meta-data
            Android:name="flutterEmbedding"
            Android:value="2" />
    </application>
</manifest>

Enfin, j'utilise les versions suivantes de Dart/Flutter/etc:

Flutter 1.12.13+hotfix.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 27321ebbad (10 weeks ago) • 2019-12-10 18:15:01 -0800
Engine • revision 2994f7e1e6
Tools • Dart 2.7.0
8
Tom O

Avec ces deux plugins, vous pouvez planifier une tâche ponctuelle et y transmettre l'emplacement actuel.

be.tramckrijte.workmanager a des limites (il indique même que tout n'est pas pris en charge). Et combiner ces deux plugins via Dart peut ne mener nulle part, car l'application n'est pas toujours en cours d'exécution. Même s'il y a un simple_callback_dispatcher_registration.Dart , qui montre comment cela fonctionne, cela rappellera le moteur Flutter et n'interrogera pas l'autre plugin.

L'écriture et la planification d'un ListenableWorker sensible à la localisation peuvent être la meilleure option disponible pour obtenir une correction GPS lors de l'exécution d'une tâche - à moins qu'il n'y ait un moyen d'interroger le plugin LocationPlugin via le moteur Flutter, tout en fonctionnant en arrière-plan. Je veux dire, si le ListenableWorker est conscient de l'emplacement, il n'a pas besoin d'obtenir l'emplacement ailleurs. Threading dans WorkManager le dit littéralement:

ListenableWorker est la classe de base pour Worker, CoroutineWorker et RxWorker. Il est destiné aux Java qui doivent interagir avec des API asynchrones basées sur le rappel telles que FusedLocationProviderClient ...

Par conséquent, ListenableWorker, Worker et CoroutineWorker seraient des classes applicables à partir desquelles s'étendre. L'équivalent pour l'implémentation iOS serait un service d'arrière-plan sensible à l'emplacement. Le moteur Flutter peut les démarrer/les arrêter, fournir des paramètres de démarrage ou recevoir des rappels, mais le code qui s'exécute en arrière-plan ne sera généralement pas Dart, car il devrait s'exécuter indépendamment.

1
Martin Zeitler