【问题标题】:How to create a service in Flutter to make an app to run always in background?如何在 Flutter 中创建服务以使应用始终在后台运行?
【发布时间】:2018-10-10 22:08:32
【问题描述】:

我想让我的 Flutter 应用程序始终在后台运行。使用 android,我们必须创建一个始终在后台运行的服务。我在 Flutter 文档中找不到有关服务的内容。

Flutter 可以做这种事情吗?

【问题讨论】:

    标签: service dart flutter


    【解决方案1】:

    目前还没有办法直接从 Flutter 中执行此操作,尽管这可能会在某些时候发生变化 - 请参阅 this bug/feature request。不过,您确实有几个选择。

    第一种是使用MethodChannels 并简单地编写您想要创建后台服务的android 代码(或者如果您希望它始终是后台服务,您可以这样做而无需来自flutter 端的通信)。

    第二个是这两个插件的某种组合 - android_alarm_managerandroid_intent。但这并不适用于所有用例。

    2021 年 2 月编辑:

    Flutter 现在支持运行后台进程。详情请见this page

    【讨论】:

    • 如果我理解得很好,MethodChannels 只能将信息从 Android 发送到 Flutter,而不能以其他方式发送?
    • 您可以使用 Method Channel 向 Android 发送信息,也可以接收信息 - 您只需从 Flutter 端 set the method call handler 并从 android 端发送消息。
    • 而且我们只能在MainActivity中使用方法通道?例如,我们不能在 Service 中使用它?
    • TBH 我不知道它是否可以通过服务工作 - 但如果您可以将方法通道的引用传递给它,我不明白为什么不 UI 是打开。一旦用户界面关闭,我不知道会发生什么。仅供参考 - 与方法通道的所有通信都必须从主线程完成。因此,如果您的服务没有在主线程上运行,您必须确保在发送消息之前切换回来。
    • @rmtmckenzie 无论如何都会在应用程序被最小化或杀死时从本机 android 调用颤振代码。我尝试了方法通道,但得到 PlatformException(NO_ACTIVITY, null, null) 错误,
    【解决方案2】:
    • 创建新的 Flutter 项目。
      • 在 android 目录中在 MainActivity 旁边创建 BroadcastReceiver 类。
      • 像这样改变MainActivity.javamain.dartAndroidManifest.xml

    我的接收者

        package com.example.flutter_broadcastreceiver_alarmmanager_repeat;
        import android.content.BroadcastReceiver;
        import android.content.Context;
        import android.content.Intent;
        public class MyReceiver extends BroadcastReceiver {
            @Override
            public void onReceive(Context context, Intent intent) {
                MainActivity.callFlutter();
            }
        }
    

    主活动

    package com.example.flutter_broadcastreceiver_alarmmanager_repeat;
    
    import android.app.AlarmManager;
    import android.app.PendingIntent;
    import android.content.Intent;
    import android.os.Bundle;
    import io.flutter.app.FlutterActivity;
    import io.flutter.plugin.common.MethodChannel;
    import io.flutter.plugins.GeneratedPluginRegistrant;
    import io.flutter.view.FlutterView;
    
    public class MainActivity extends FlutterActivity {
    
        private PendingIntent pendingIntent;
        private AlarmManager alarmManager;
        private static  FlutterView flutterView;
        private static final String CHANNEL = "com.tarazgroup";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
            flutterView=getFlutterView();
            GeneratedPluginRegistrant.registerWith(this);
    
            Intent intent = new Intent(this, MyReceiver.class);
            pendingIntent = PendingIntent.getBroadcast(this, 1019662, intent, 0);
            alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
            alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000, pendingIntent);
    
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            alarmManager.cancel(pendingIntent);
        }
    
        static void callFlutter(){
            MethodChannel methodChannel=new MethodChannel(flutterView, CHANNEL);
            methodChannel.invokeMethod("I say hello every minute!!","");
        }
    }
    

    main.dart

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key}) : super(key: key);
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
    
      static const methodChannel = const MethodChannel('com.tarazgroup');
    
      _MyHomePageState() {
        methodChannel.setMethodCallHandler((call) {
          print(call.method);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
          ),
          body: Container() 
        );
      }
    }
    

    AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.flutter_broadcastreceiver_alarmmanager_repeat">
    
        <application
            android:name="io.flutter.app.FlutterApplication"
            android:icon="@mipmap/ic_launcher"
            android:label="flutter_broadcastreceiver_alarmmanager_repeat">
            <activity
                android:name=".MainActivity"
                android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
                android:hardwareAccelerated="true"
                android:launchMode="singleTop"
                android:theme="@style/LaunchTheme"
                android:windowSoftInputMode="adjustResize">
    
                <meta-data
                    android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                    android:value="true" />
    
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <receiver android:name=".MyReceiver"></receiver>
    
        </application>
    
    </manifest>
    

    您的颤振代码每 1 分钟调用一次。甚至您的应用程序已最小化或切换到另一个应用程序或屏幕关闭。

    【讨论】:

    • 后台服务将被限制为每 15 分钟检查一次。如果我使用上面的代码,如果应用程序处于后台以节省电池,操作系统是否也会限制为 15 分钟?
    • 不幸的是,可能需要 15 分钟或更少或更多,取决于您的 android 操作系统。如果您的应用程序进入打盹模式。运行后台服务没有一定的条件。
    • 嘿,有没有办法定期运行一些代码(并生成通知),即使 Flutter 应用程序被杀死?
    • @geeekfa 我们如何为 ios 做到这一点?
    • @AbhishekMehandiratta 要生成通知,您可以使用推送通知服务,如 Firebase 云消息 (FCM)。即使您的应用被终止,它也可以生成通知
    【解决方案3】:

    这是一个可以做到这一点的库background_fetch

    Background Fetch 是一个非常简单的插件,它大约每 15 分钟会在后台唤醒一个应用程序,提供较短的后台运行时间。每当后台获取事件发生时,此插件将执行您提供的 callbackFn。 ref

    【讨论】:

    • 如果应用没有运行,这在 iOS 上是否仍然有效?
    • @user2233706 yes* 您可以在包的文档中阅读更多内容。写的很好。
    • 它没有。由于 iOS 的限制,后台提取仅在应用未被用户终止时才有效。
    • @user2233706 这不是解决方案,始终运行 = 每 15 分钟一次? :D
    【解决方案4】:

    flutter_background_service 与来自dart:asyncTimer 结合。

    见:

    【讨论】:

      猜你喜欢
      • 2011-02-03
      • 2018-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-01
      • 1970-01-01
      相关资源
      最近更新 更多