如何判断Flutter后台服务中的应用程序从后台返回到前台?

huangapple go评论60阅读模式
英文:

Flutter Background Service how to judge apps goes back from background to foreground?

问题

I understand that you want a translation of the code-related content. Here's the translation:

我明白您需要代码相关内容的翻译。以下是翻译:

我想要实现的目标:

- 判断应用程序在后台或前台运行,位于后台服务内部;

我尝试过的:

- 使用WidgetBindingObserver检测应用程序的生命周期时,发现无法更改后台服务;

这是我目前用于检测应用程序生命周期的方法:

```dart
 @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    debugPrint('App State = $state');
    DbUserUranus? user = AppData.instance()!.user;
    if (user != null) {
      if (state == AppLifecycleState.inactive ||
          state == AppLifecycleState.paused) {
        ChatApp.instance()!
            .eventsSender
            .sendPresence(PresenceType.Away, user.user_addr);
        GlobalSettings.isAppInBackground = true;
        debugPrint("--------------- ^^^^^^^^^^ 应用在后台运行!!");


      } else if (state == AppLifecycleState.resumed) {
        ChatApp.instance()!
            .eventsSender
            .sendPresence(PresenceType.Available, user.user_addr);
        GlobalSettings.isAppInBackground = false;
        debugPrint("--------------- ^^^^^^^^^^^ 应用恢复前台!");
      }
    }
  }

问题是,我从主线程更改的值在后台服务线程中不生效。

以下是如何启动后台服务的方法:

if (Platform.isAndroid) {
    debugPrint("现在... 启动后台服务!!!");
    var channel = const MethodChannel('com.example/background_service');
    var callbackHandle =
    PluginUtilities.getCallbackHandle(backgroundMain);
    channel.invokeMethod('startService', callbackHandle?.toRawHandle());
  }

如果有任何建议,将不胜感激!


Please note that I've translated the code and comments, but I haven't addressed any specific questions or issues mentioned in the code. If you have further questions or need assistance with any part of the code, please feel free to ask.

<details>
<summary>英文:</summary>

what I want achieve:

- judge app is in background or foreground inside a Background service;


what I have tried:

- I found is not possible to change background service while using WidgetBindingObserver to detect app&#39;s cycle;


this is my currently to detect apps cycle:

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
debugPrint('App State = $state');
DbUserUranus? user = AppData.instance()!.user;
if (user != null) {
if (state == AppLifecycleState.inactive ||
state == AppLifecycleState.paused) {
ChatApp.instance()!
.eventsSender
.sendPresence(PresenceType.Away, user.user_addr);
GlobalSettings.isAppInBackground = true;
debugPrint("----------------- ^^^^^^^^^^ App in background!!");

  } else if (state == AppLifecycleState.resumed) {
    // MQTTClientWrapper.instance().connect();

    ChatApp.instance()!
        .eventsSender
        .sendPresence(PresenceType.Available, user.user_addr);
    GlobalSettings.isAppInBackground = false;
    debugPrint(&quot;---------------- ^^^^^^^^^^^ App comes back!&quot;);
  }
}

}


the questions is , the value I changed from main thread, doesn not takes effect in background service thread.

This is How can start a background service:

if (Platform.isAndroid) {
debugPrint("now... to the background service!!!");
var channel = const MethodChannel('com.example/background_service');
// var serviceInst = MQTTService.instance();
// var backgroundMain = serviceInst.startListen();
var callbackHandle =
PluginUtilities.getCallbackHandle(backgroundMain);
channel.invokeMethod('startService', callbackHandle?.toRawHandle());
}


any suggestions would be many thanks to!

</details>


# 答案1
**得分**: 1

I used a Isolate and the flutter_background_service plugin.

You can achieve this with the following:

1. 在你的后台服务中使用一个带有ValueNotifier的接收器,它将接收来自UI的事件。
2. 在你的后台服务中使用一个带有ValueNotifier的布尔值,用于跟踪应用程序的状态。
3. 在你的UI中,将你的小部件扩展为WidgetsBindingObserver类,用于捕获应用程序生命周期事件(恢复,暂停,不活动等)。
4. 在initState方法中注册你的观察器。
5. 覆盖didChangeAppLifecycleState事件,这将在应用程序的生命周期状态发生变化时触发,它具有一个状态参数,你可以使用它来获取应用程序的当前状态。

UI类:

```dart
class _ExampleState extends State<Example> with WidgetsBindingObserver {
  SendPort? _sendPort;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    // ...
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    _sendPort ??= IsolateNameServer.lookupPortByName('appState');
    switch (state) {
      case AppLifecycleState.resumed:
        _sendPort?.send(false);
        break;
      case AppLifecycleState.paused:
        _sendPort?.send(true);
        break;
      default:
        break;
    }
  }
}

服务:

class RideBackgroundService {
  static final ValueNotifier<bool> _appPausedState = ValueNotifier<bool>(false);
  static final ValueNotifier<ReceivePort?> _stateNotifier =
      ValueNotifier<ReceivePort?>(null);

  // ...

  static void onStart(ServiceInstance service) {
    if (_stateNotifier.value == null) {
      _stateNotifier.value = ReceivePort('appState');
      IsolateNameServer.registerPortWithName(
          _stateNotifier.value!.sendPort, 'appState');
      _stateNotifier.value?.listen((dynamic value) {
        _appPausedState.value = value as bool;
      });
    }
    // ...
  }
}

然后,你可以使用_appPausedState.value来查看应用程序的状态。请注意,无法发送实例,你应该使用基本数据类型,如int,或者你可以将对象转换为地图并发送。

你也可以通过一个无状态小部件来实现这一点,我认为它不必是有状态的小部件,我只是在我的项目中使用了有状态的小部件。

你还可以修改这段代码,以发送int而不是bool以获取所有状态。

英文:

I used a Isolate and the flutter_background_service plugin.

You can achieve this with the following:

  1. Setup a receiver with a ValueNotifier in your background service, this will receive the events from the UI
  2. Setup a bool with a ValueNotifier in your background service this will be used to keep track of the state of the application.
  3. In your UI, extends your widget with the WidgetsBindingObserver class, this will be used to catch application lifecycle events(resume, pause, inactive, etc)
  4. Register your observer in the initState method.
  5. override the didChangeAppLifecycleState event, this will be triggered once the lifecycle state of the application has been changed, this has a state parameter that you can use to get the current state of the app.

The UI Class:

class _ExampleState extends State<Example> with WidgetsBindingObserver{

SendPort? _sendPort;

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addObserver(this);
  ...
}

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  super.didChangeAppLifecycleState(state);
  _sendPort ??= IsolateNameServer.lookupPortByName(&#39;appState&#39;);
  switch(state){
    case AppLifecycleState.resumed:
      _sendPort?.send(false);
      break;
    case AppLifecycleState.paused:
      _sendPort?.send(true);
      break;
    default:
      break;
  }
}

The Service:

class RideBackgroundService {
  static final ValueNotifier&lt;bool&gt; _appPausedState = ValueNotifier&lt;bool&gt;(false);
  static final ValueNotifier&lt;ReceivePort?&gt; _stateNotifier = ValueNotifier&lt;ReceivePort?&gt;(null);

  ...

  static void onStart(ServiceInstance service) {
    if(_stateNotifier.value == null){
      _stateNotifier.value = ReceivePort(&#39;appState&#39;);
      IsolateNameServer.registerPortWithName(_stateNotifier.value!.sendPort, &#39;appState&#39;);
      _stateNotifier.value?.listen((dynamic value) {
        _appPausedState.value = serviceInfo.value as bool;
      });
    }
    ...
  }

Then you can use the _appPauseState.value to see the state of the application. Please keep in mind, its not possible to send instances, you should rather use basic data types like a int, or you could convert the object to a map and send that.

You can also do this via a stateless widget, i dont think it has to be a statefull widget, I just used a statefull widget in my project.

You can maybe also modify this code to send an int instead of a bool to get all of the states.

huangapple
  • 本文由 发表于 2023年5月21日 10:15:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76298031.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定