如何使Flutter Workmanager插件和Location插件协同工作



1。问题描述

我的目标是建立一个Flutter应用程序,使用这个工作管理器插件和这个位置插件定期更新位置。但是当Workmanager回调触发时,我无法正确加载Location插件。我得到这个错误:

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

所以基本上,问题是当Workmanager插件试图运行dart代码时,它不会加载Location插件。

2.我研究过的其他资源

我发现其他人也面临着同样的问题,在这里,在这里。

据我所知,为这些问题提供的解决方案可以归结为:创建一个名为CustomApplication.java的文件,该文件扩展了FlutterApplication,并注册您的插件。然后在AndoidManifest.xml文件中注册CustomApplication.java文件。

3.到目前为止我的代码

我试着制作一个最低限度的应用程序,实现我需要的功能:

  1. 我实现了Workmanager插件(运行良好(
  2. 我实现了Location插件(运行良好(
  3. 我试图结合这些功能(不起作用(

要查看我在每一步都做了什么,请查看此处:https://gitlab.com/tomoerlemans/workmanager_with_location/-/commits/master.(此存储库也可用于快速复制问题(。

相关代码文件如下:

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

CustomApplication.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>

最后,我正在运行以下版本的Dart/Flutter等:

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

我还没有尝试过什么后台位置跟踪,但考虑到你只想从后台进行位置跟踪,我认为工作管理器+地理定位器会起作用。你应该看到这个项目ha_client,它正在使用workmanager和geolocator来获得coords

此外,您正在使用的位置包有自己的背景位置跟踪,这是在实验性的背景位置更新中。

使用这两个插件,您可以安排一个一次性任务并将当前位置传递给它

be.tramckrijte.workmanager有局限性(它甚至表示并非所有内容都得到支持(。通过Dart组合这两个插件可能不会有任何结果,因为应用程序并不总是在运行。即使有一个simple_callback_dispatcher_registration.dart显示了它的工作原理,它也会回调Flutter引擎,而不会查询其他插件。

写作&调度一个位置感知的ListenableWorker可能是在运行任务时获得GPS定位的最佳选择,除非有任何方法可以在后台运行时通过Flutter引擎查询LocationPlugin插件。我的意思是,如果ListenableWorker是位置感知的,那么它不需要在其他地方获得位置。WorkManager中的线程实际上表明:

ListenableWorkerWorkerCoroutineWorkerRxWorker的基类。它适用于那些必须与基于回调的异步API(如FusedLocationProviderClient…(交互的Java开发人员。。。

因此,ListenableWorkerWorkerCoroutineWorker将是可扩展的适用类。iOS实现的等价物将是一个位置感知后台服务。Flutter引擎可以启动/停止它们,提供启动参数或接收回调,但在后台运行的代码通常不会是Dart,因为它应该独立运行。

最新更新