如何使用 Flutter 让后台应用每天下午 2 点打开一个对话框?

问题描述

我希望应用每天下午 2 点打开一个对话框,即使用户正在做其他事情并且此时当前不在应用界面中。

如何使用 Flutter 做到这一点,使其同时适用于 Android 和 iOS?

我正在考虑这样的方法

timer = Timer.periodic(Duration(seconds: 60),(Timer t) => checkTime());

每分钟检查一次是否在 14:00:00 和 14:00:59 之间,方法类似于 flutter run function every x amount of seconds,但这似乎是一种资源浪费:可能有更好的、更自然的方式在精确的时间在后台唤醒应用程序?

问题:如何让应用在每天下午 2 点显示对话框,即使用户正在做其他事情/在另一个应用中,使用 Flutter?

注意:如果设备关闭了屏幕(即用户没有使用手机),则下次打开屏幕时应显示该对话框。

解决方法

我的解决方案是使用 workmanager (当前版本 0.4.1) flutter 包来实现您正在寻找的要求。由于它由 Flutter 社区团队 维护,我们可以期待长期支持。

基于他们的文档

Flutter WorkManager 是 Android's WorkManageriOS' performFetchWithCompletionHandler 的包装器,有效地实现了无头 在后台执行 Dart 代码。

这对于运行周期性任务特别有用,例如定期获取远程数据。

Android 工作管理器将自动根据其运行版本的操作系统级别自行处理后台进程

来到 iOS,根据他们的文档,此包用于执行后台操作的功能已被弃用。但是Flutter 社区团队已准备好根据他们在 GitHub 存储库中的 comment 在即将发布的版本中推送替换。因此升级到最新版本将有助于您解决此问题。

初始化 wrokmanager 内的 main()

Workmanager().initialize(
      callbackDispatcher,// The top level function,aka callbackDispatcher
      isInDebugMode:
          true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
      );

然后像下面这样安排任务

Workmanager().registerOneOffTask("1","simpleTask_1",initialDelay: Duration(seconds: 15)); 
Workmanager().registerPeriodicTask("102","simplePeriodicTask_1",initialDelay: Duration(seconds: 15),frequency: Duration(minutes: 15)); // Set your 2 PM frequency here. 

有关安排任务的可用选项,请参阅 documentation and setup

并定义一个 callbackDispatcher 并且它需要是静态函数或顶级函数才能根据文档作为 Flutter 入口点访问。

//Defined outside main()
void callbackDispatcher() {
  Workmanager().executeTask((task,inputData) {
    print(
        "Native called background task: callbackDispatcher"); //simpleTask will be emitted here.
    createNotify(); // Created a local notification to make sure everything works in background - In your case show alert here.
    return Future.value(true);
  });
}

// your main() goes here

不是显示通知,而是向下推导逻辑以显示警报弹出窗口。

编辑 1:

在评论中回答您的问题

如何查看程序每天14:00:00左右只运行一次 下午?

计划的工作存储在内部管理的 SQLite 数据库中,WorkManager 负责确保此工作持续,并在设备重启后重新安排。并且会多次执行,直到手动取消。

除此之外,出于开发目的,您可以设置最小间隔并验证一次,例如 1 小时。工作经理支持的最小间隔为 15 分钟。即使您设置的时间低于该阈值时间,15 分钟也会被设为默认值。

您的代码似乎定期运行,但它并没有以 具体时间。

目前尚不支持开箱即用。但我们可以在安排任务时利用初始延迟。计算当前时间与您要触发的时间之间的时间差。 例如,如果我想在 9 点钟安排某个任务,并且在安排任务时您可以执行以下操作 当应用程序在 7 点钟打开时,将初始延迟设置为两个小时。 请参阅与此相关的 stackoverflow answer

还有,用这个方法,你确定不会自动关闭 通过操作系统节省电池?

根据 WorkManager doc,执行将在我们指定的时间间隔内发生。由于WorkManager 受操作系统电池优化的影响,例如doze mode,我们需要注意执行可能会延迟但不会取消。

最后一件事:后台进程可以在没有用户的情况下启动弹出窗口吗 相互作用? (其他答案提到这不太可能)。谢谢 又来了!

对我来说这是棘手的部分。从技术上讲,我们无法在没有用户后台操作的情况下显示 UI。因为,UI 需要基于 context 显示给用户。因此,来到 dart 点,可能很难显示您正在寻找的一些警报弹出窗口。但如果您只想显示某种信息,您可以尝试FlutterToast,因为它会从后台显示给用户。

编辑 2

您希望在应用未打开时显示警报的要求称为 Draw over other apps 并且它仅适用于 Android我猜是因为 iOS 中没有这样的功能。

您可以在 flutter 中引用此 package 以根据您的要求显示警报。将 WorkManager 与此包实现相结合以实现您想要的结果。我还没有尝试过这个包,但如果你遇到任何问题,请告诉我。

快乐编码!!

,

TL,DR: 使用 alarm_manager_service。它可以在特定时间安排任务,如果需要,可以在从该开始时间开始设定的持续时间后重复执行。


您打算在主应用程序中包含完整代码。问题是,无法保证该应用程序不会被 Android 操作系统关闭。使用电源管理功能(打瞌睡),该应用程序可能会在数小时内被杀死。最近的 android 版本为用户提供了明确的控制。用户将不得不原谅这个应用程序的电源优化(禁用电池优化)。即便如此,RAM 管理可能会在危急情况下杀死应用程序。这是预期的行为。


现在,关于将界面显示到前台:Android 似乎也阻止了侵入性应用行为,例如将 Activity 带到前台,这会在用户与另一个 Activity 交互时阻碍用户。见https://developer.android.com/guide/components/activities/background-starts。它还列出了例外情况,您可以借此实现这一目标。 (我知道 Facebook/messenger 过去常常这样做,即使在不尊重建议的情况下杀死任务之后,也会进行各种后台活动。但是,我们大多数人都讨厌 FB 应用程序太具有侵入性,并且给我留下了不道德的印象)。

连续运行应用程序只是为了检查一天中的特定时间肯定是在浪费资源。


因此,除了主 GUI 应用程序代码之外,您还必须注册一个 background service。 Dart 应用程序作为隔离运行。 You can create a separate isolate 用于除用户交互的主 GUI 之外的后台服务。现在,即使主 GUI 隔离关闭,后台服务也可以运行。

在执行这些操作时,您还需要修改 android 清单文件。我可以给你指出一些线索: 使用 alarm_manager_service 在设定的时间安排后台任务。这可以回调以执行活动并显示对话框或唤醒应用程序。

另一个:workmanager

Read this quick and to the point summary 作为自述文件描述给出,以了解 android 操作系统中的现有场景。这实际上总结了我通过无数 developer.android 文章阅读的所有内容。

示例代码:

您可以使用闹钟管理器来安排特定时间的任务,也可以从开始时间开始在设定的持续时间后重复它~> 某天下午 2 点开始,每 24 小时定期重复一次。

使用 alarm_manager 的代码给出了 here 作为答案(解决方案 1)。

,

如果您打算为 iOS 设备用户自动打开应用程序。好吧,那是不可能的。我不确定 Android 是否也可以。

您可能希望创建一个在 14:00 推送的通知,询问用户是否要打开对话框。

这是 FlutterFire 中通知的文档链接:Notifications

和有关 Flutter 小部件的文档(为此您需要 Xcode):Develop an iOS 14 Widget in Flutter with SwiftUI