You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中Workmanager后台无法通过Geolocator获取定位的解决方法

后台使用Workmanager+Geolocator获取定位报错解决方法

我用Workmanager在后台通过Geolocator获取定位,前台运行无异常,但后台触发任务时出现任务被取消的报错,相关代码与报错信息如下:

实现代码

import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:workmanager/workmanager.dart';

late LocationSettings locationSettings;

const fetchBackground = "fetchBackground";

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    switch (task) {
      case fetchBackground:
        try {
          Position position = await Geolocator.getCurrentPosition();
          print(
              'Latitude: ${position.latitude}, Longitude: ${position.longitude}');
        } catch (error) {
          print('Error getting position: $error');
        }
        break;
    }
    return Future.value(true);
  });
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  Workmanager().initialize(callbackDispatcher);
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MyApp Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<Position> _determinePosition() async {
    bool serviceEnabled;
    LocationPermission permission;

    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return Future.error('Location services are disabled.');
    }

    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        return Future.error('Location permissions are denied');
      }
    }

    if (permission == LocationPermission.deniedForever) {
      return Future.error(
          'Location permissions are permanently denied, we cannot request permissions.');
    }

    return await Geolocator.getCurrentPosition();
  }

  void _getPosition() async {
    try {
      Position position = await _determinePosition();
      print('Latitude: ${position.latitude}, Longitude: ${position.longitude}');
    } catch (error) {
      print('Error getting position: $error');
    }
  }

  @override
  void initState() {
    super.initState();
    _getPosition();

    Workmanager().initialize(
      callbackDispatcher,
      isInDebugMode: true,
    );
    Workmanager().registerPeriodicTask(
      "backgroundLocationTask",
      fetchBackground,
      frequency: Duration(seconds: 10),
      //frequency: Duration(minutes: 15), // Set every 15 minutes
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
    );
  }
}

后台报错信息

D/FlutterGeolocator(19802): Disposing Geolocator services
E/FlutterGeolocator(19802): Geolocator position updates stopped
D/FlutterGeolocator(19802): Stopping location service.
I/WM-WorkerWrapper(19802): Work [ id=fc438a55-d63a-47e7-836e-5e0bfac59d2d, tags={ be.tramckrijte.workmanager.BackgroundWorker } ] was cancelled
I/WM-WorkerWrapper(19802): java.util.concurrent.CancellationException: Task was cancelled.
I/WM-WorkerWrapper(19802):  at androidx.work.impl.utils.futures.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1184)
I/WM-WorkerWrapper(19802):  at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:514)
I/WM-WorkerWrapper(19802):  at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
I/WM-WorkerWrapper(19802):  at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:311)
I/WM-WorkerWrapper(19802):  at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
I/WM-WorkerWrapper(19802):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
I/WM-WorkerWrapper(19802):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
I/WM-WorkerWrapper(19802):  at java.lang.Thread.run(Thread.java:919)
D/FlutterGeolocator(19802): Unbinding from location service.
D/FlutterGeolocator(19802): Destroying location service.
D/FlutterGeolocator(19802): Stopping location service.
D/FlutterGeolocator(19802): Destroyed location service.

解决方法

1. 补全后台定位权限

  • Android端:在android/app/src/main/AndroidManifest.xml中添加后台定位权限:
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    
  • iOS端:在ios/Runner/Info.plist中添加后台定位描述:
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>需要后台定位权限以获取您的位置信息</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>需要后台定位权限以获取您的位置信息</string>
    
  • 代码中请求后台权限:修改_determinePosition方法,明确请求永久定位权限:
    permission = await Geolocator.requestPermission(locationPermissionLevel: LocationPermissionLevel.locationAlways);
    

2. 修正Workmanager任务配置

  • 移除initState中重复的Workmanager().initialize调用(main方法已初始化)。
  • 配置任务约束条件,避免系统轻易取消任务,同时遵守Android周期任务最小间隔要求(15分钟):
    Workmanager().registerPeriodicTask(
      "backgroundLocationTask",
      fetchBackground,
      frequency: const Duration(minutes: 15),
      constraints: const Constraints(
        networkType: NetworkType.not_required,
        requiresBatteryNotLow: false,
        requiresCharging: false,
        requiresDeviceIdle: false,
        requiresStorageNotLow: false,
      ),
      backoffPolicy: BackoffPolicy.exponential,
      backoffPolicyDelay: const Duration(seconds: 10),
    );
    
  • 调试时可改用registerOneOffTask配合initialDelay测试后台执行:
    Workmanager().registerOneOffTask(
      "testBackgroundLocation",
      fetchBackground,
      initialDelay: const Duration(seconds: 10),
    );
    

3. 适配Geolocator后台定位参数

在后台任务的定位请求中指定适配后台的LocationSettings,提升定位成功率:

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    switch (task) {
      case fetchBackground:
        try {
          LocationSettings locationSettings = const LocationSettings(
            accuracy: LocationAccuracy.balanced,
            distanceFilter: 10,
          );
          Position position = await Geolocator.getCurrentPosition(locationSettings: locationSettings);
          print('Latitude: ${position.latitude}, Longitude: ${position.longitude}');
        } catch (error) {
          print('Error getting position: $error');
        }
        break;
    }
    return Future.value(true);
  });
}

4. 适配系统后台限制

  • Android 12+对后台任务执行时间有严格限制,确保定位逻辑尽量简洁,减少耗时。
  • 部分厂商(如小米、华为)有额外的后台管控,需在系统设置中为应用开启"后台运行权限"或"自启动权限"。

内容的提问来源于stack exchange,提问作者user572575

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 22:24:55