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:
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
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 pourWorker
,CoroutineWorker
etRxWorker
. Il est destiné aux Java qui doivent interagir avec des API asynchrones basées sur le rappel telles queFusedLocationProviderClient
...
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.