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

Flutter后台进程中SharedPreferences返回null问题求助

问题描述

我使用flutter_background_service插件实现应用后台图片处理功能,处理所需的参数已提前存入SharedPreferences,但在processImage方法中,获取SharedPreferences实例并执行reload后,所有参数均返回null,只能使用默认值。

相关代码

初始化后台服务代码

Future<void> initializeService() async {
  final service = FlutterBackgroundService();
  // 创建Android通知渠道
  const AndroidNotificationChannel channel = AndroidNotificationChannel(
    notificationChannelId, // ID
    'IMAGE STAMPING SERVICE', // 标题
    description: '此渠道用于重要通知',
    importance: Importance.high, // 设置重要性
  );
  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();
  // 创建通知渠道
  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>()
      ?.createNotificationChannel(channel);
  await service.configure(
    androidConfiguration: AndroidConfiguration(
      onStart: onStart,
      autoStart: true,
      isForegroundMode: true,
      notificationChannelId: notificationChannelId,
      initialNotificationTitle: '图片盖章服务',
      initialNotificationContent: '初始化中',
      foregroundServiceNotificationId: notificationId,
    ),
    iosConfiguration: IosConfiguration(
      autoStart: true,
      onForeground: onStart,
    ),
  );
}

/// 初始化图片目录监听器
void initializePhotoWatcher() async {
  try {
    // 初始化SharedPreferences
    SharedPreferences prefs = await SharedPreferences.getInstance();

    // 获取权限状态
    bool arePermissionsGranted = prefs.getBool('permissionsGranted') ?? false;

    if (arePermissionsGranted) {
      photoDirectory = Directory(AppConstants.directoryPath);

      if (await photoDirectory?.exists() ?? false) {
        final watcher = DirectoryWatcher(photoDirectory!.path);
        print("已为路径创建目录监听器: ${photoDirectory!.path}");
        watcher.events.listen((event) {
          print("检测到事件: ${event.toString()}");
          if (event.type == ChangeType.ADD &&
              !event.path.contains("stamped_") &&
              !event.path.contains(".pending")) {
            print("调度文件处理: ${event.path}");
            // 调用后台服务
            final service = FlutterBackgroundService();
            service.invoke('processImage', {'imagePath': event.path});
          }
        });
      } else {
        print("相机目录不存在或无法访问");
      }
    } else {
      // 请求权限并等待授权
      bool permissionsGranted = await requestPermissions();
      if (permissionsGranted) {
        // 权限已授权,继续初始化
        photoDirectory = Directory(AppConstants.directoryPath);
        // 剩余代码...
      } else {
        // 权限未授权,进行相应处理
        print("需要更多权限");
      }
    }
  } catch (e) {
    print(e.toString());
  }
}

Future<void> onStart(ServiceInstance service) async {
  // DartPluginRegistrant.ensureInitialized();
  // final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
  //     FlutterLocalNotificationsPlugin();
  // 设置前后台状态切换监听
  if (service is AndroidServiceInstance) {
    service.on('processImage').listen((data) {
      final String imagePath = data?['imagePath'];
      service.setAsBackgroundService();
      // 执行图片处理
      processImage(imagePath);
    });
    service.on('setAsForeground').listen((event) {
      service.setAsForegroundService();
    });
    service.on('setAsBackground').listen((event) {
      service.setAsBackgroundService();
    });
  }
  service.on('stopService').listen((event) {
    service.stopSelf();
  });
}

图片处理代码

void processImage(String imagePath) async {
  try {
    final File imageFile = File(imagePath);
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    // 按其他方案建议执行重载
    await prefs.reload();
    // 获取用户偏好设置
    // 所有参数都返回null,只能使用默认值
    int fontSize = prefs.getInt('fontSize') ?? AppConstants.fontSize;
    int scaledFontSize = fontSize * 2;
    final Color fontColor = _getFontColorFromString(prefs);
    // 获取文本位置
    String textPositionString =
        prefs.getString('textPlacement') ?? AppConstants.textPosition.name;
    TextPosition textPosition = TextPosition.values.firstWhere(
        (e) => e.name == textPositionString,
        orElse: () => AppConstants.textPosition // 未找到时使用默认值
        );
    String text = prefs.getString('text') ?? AppConstants.text;
    // 检查文件是否存在且不是临时文件
    if (await imageFile.exists() && !imageFile.path.contains('.pending-')) {
      // 调用图片盖章函数
      await addStampToPhoto(
        image: imageFile,
        fontSize: scaledFontSize,
        text: text,
        fontColor: fontColor,
        textPosition: textPosition,
        photoDirectoryPath: AppConstants.directoryPath,
      );
    } else {
      // 记录日志并处理,比如稍后重试
      print('文件不存在或为临时文件: $imagePath');
    }
  } catch (e) {
    print('处理文件 $imagePath 时出错: $e');
    // 可选:实现重试机制或其他错误处理
  }
}
解决方案

1. 初始化Dart插件注册器

后台服务运行在独立的Isolate中,必须初始化插件注册器才能正确访问SharedPreferences。取消onStart方法中相关注释:

Future<void> onStart(ServiceInstance service) async {
  DartPluginRegistrant.ensureInitialized(); // 必须添加此行
  // ... 剩余代码
}

2. 直接传递参数到后台服务(替代方案)

如果初始化注册器后问题仍存在,可以在调用服务时直接传递所需参数,避免在后台读取SharedPreferences:

// 在initializePhotoWatcher的事件监听中,先获取参数再传递
watcher.events.listen((event) {
  print("检测到事件: ${event.toString()}");
  if (event.type == ChangeType.ADD &&
      !event.path.contains("stamped_") &&
      !event.path.contains(".pending")) {
    print("调度文件处理: ${event.path}");
    // 先获取SharedPreferences中的参数
    SharedPreferences.getInstance().then((prefs) {
      int fontSize = prefs.getInt('fontSize') ?? AppConstants.fontSize;
      String textPositionString = prefs.getString('textPlacement') ?? AppConstants.textPosition.name;
      String text = prefs.getString('text') ?? AppConstants.text;
      // 传递所有需要的参数
      final service = FlutterBackgroundService();
      service.invoke('processImage', {
        'imagePath': event.path,
        'fontSize': fontSize,
        'textPlacement': textPositionString,
        'text': text,
        // 其他需要的参数(如字体颜色)
      });
    });
  }
});

修改onStart方法接收参数:

service.on('processImage').listen((data) {
  final String imagePath = data?['imagePath'];
  final int fontSize = data?['fontSize'] ?? AppConstants.fontSize;
  final String textPositionString = data?['textPlacement'] ?? AppConstants.textPosition.name;
  final String text = data?['text'] ?? AppConstants.text;
  service.setAsBackgroundService();
  // 直接传递参数给processImage
  processImage(imagePath, fontSize, textPositionString, text);
});

更新processImage方法接收参数:

void processImage(String imagePath, int fontSize, String textPositionString, String text) async {
  try {
    final File imageFile = File(imagePath);
    int scaledFontSize = fontSize * 2;
    // 直接使用传递的参数,无需再读取SharedPreferences
    final Color fontColor = // 若需字体颜色,同样在invoke时传递参数
    TextPosition textPosition = TextPosition.values.firstWhere(
        (e) => e.name == textPositionString,
        orElse: () => AppConstants.textPosition
        );
    // ... 后续处理代码
  } catch (e) {
    print('处理文件 $imagePath 时出错: $e');
  }
}

3. 检查SharedPreferences存储时机

确保参数在后台服务启动前已完成写入,且存储操作是异步完成的,避免后台服务读取时数据尚未持久化。


内容的提问来源于stack exchange,提问作者who-aditya-nawandar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 16:28:14