如何让 Flutter Workmanager 插件和 Location 插件一起工作

Posted

技术标签:

【中文标题】如何让 Flutter Workmanager 插件和 Location 插件一起工作【英文标题】:How to make Flutter Workmanager plugin and Location plugin work together 【发布时间】:2020-06-01 09:12:55 【问题描述】:

1.问题描述

我的目标是构建一个 Flutter 应用程序,该应用程序使用 this workmanager plugin 和 this location plugin 获取定期位置更新。但是当我的 Workmanager 回调触发时,我无法正确加载 Location 插件。我收到此错误:

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

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

2。我研究过的其他资源

我发现其他人也面临同样的问题,here、here 和 here。

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

3.到目前为止我的代码

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

    我实现了 Workmanager 插件(工作正常) 我实现了 Location 插件(工作正常) 我试图结合这些功能(不起作用)

要确切了解我在每个步骤中所做的工作,请查看此处:https://gitlab.com/tomoerlemans/workmanager_with_location/-/commits/master。 (此存储库也可用于快速复制问题)。

相关代码文件如下:

ma​​in.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/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

【问题讨论】:

你实现成功了吗? 您好,您找到解决方案了吗? android:name="io.flutter.plugins.CustomApplication" 你的清单有问题。使用正确的路径,如果它在根目录中,那么只需 android:name=".CustomApplication" 【参考方案1】:

我还没有尝试过什么后台位置跟踪,但考虑到您只想从后台进行位置跟踪,那么我认为workmanager + geolocater 会起作用。 你应该看到这个项目ha_client 它正在使用 workmanager 和 geolocater 来获取坐标

此外,您使用的位置包有它自己的后台位置跟踪,它处于实验性Background Location Updates 中。

【讨论】:

【参考方案2】:

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

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

编写和安排位置感知 ListenableWorker 可能是在运行任务时获得 GPS 定位的最佳选择 - 除非有任何方法可以通过 Flutter 引擎查询 LocationPlugin 插件,同时在背景。我的意思是,如果ListenableWorker 是位置感知的,它不需要在其他地方获取位置。 Threading in WorkManager 字面意思是:

ListenableWorkerWorkerCoroutineWorkerRxWorker 的基类。它适用于必须与基于回调的异步 API 交互的 Java 开发人员,例如 FusedLocationProviderClient ...

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

【讨论】:

“它甚至声明并非所有内容都受到支持”是一种笼统的声明。你能告诉我它在哪里声明我正在寻找的功能是不可能的吗? 这种思维方式有点荒谬;这就像假装某个虚构的实体存在一样,只是因为无法证明它不存在-WorkManager 文档非常清楚,不管你喜不喜欢。如果假设抽象将负责任何本机或 JVM 实现(否则不会有 androidios 目录),那么您可能还没有完全理解 Flutter 的实际含义……所有这些插件都可以正在按名称安排工作任务并最终收到回调。 一次性任务没问题,因为抽象层可以直接传递 lat/lng - 但是对于周期性任务,最好的情况下你可能会得到当工人任务已被安排。任何使用过WorkManager 的人都可能会建议使用位置感知Worker - 唯一的替代方案可能是flutter_geofire,它最终也有限制,而应用程序没有运行。 workmanager 插件仓库的主要贡献者之一,实际上谈到了从 workmanager 回调中运行 Flutter 插件:github.com/vrtdev/flutter_workmanager/issues/6。我在原始问题中引用了这一点。

以上是关于如何让 Flutter Workmanager 插件和 Location 插件一起工作的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 工作管理器返回“工作人员结果失败 [id=XXXXxXXXXXXXXXX 标签= be.tramckrijte.workmanager.BackgroundWorker ]”

如何在 Flutter 应用程序的后台运行代码?

WorkManager 在多进程应用中的高级用法

WorkManager 在多进程应用中的高级用法

WorkManager 在多进程应用中的高级用法

Android Jetpack WorkManager 介绍