发布于 2026-01-06 3 阅读
0

Flutter:后台服务 rest DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

Flutter:后台服务

休息

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

所以,是时候真正尝试一下 Flutter 了。我看了最近的开发者大会,Flutter 的功能和未来发展方向(Web、Windows、Linux)都让我惊叹不已。如果你想看看那次大会,这里是YouTube 链接

背景

我从不做简单的“Hello World”程序,我总是直接挑战高难度项目,这样才能真正了解我正在尝试的东西的优缺点。于是,我从我的“百万富翁创意清单”里挑了一个点子开始了这段旅程。

该应用:Rest

我会开发一个非常简单的应用,对我来说超级实用。我患有梅尼埃病引起的耳鸣(顺便说一句,如果你也患有这种病,请留言),所以有时候我喜欢在睡觉时听点背景音乐。我不想让手机整晚都播放音乐。

研究

每次想到绝妙的点子,我都会上网搜索。我不想重复造轮子。这次我找到一个能满足需求的应用程序。问题是它经常卡顿或崩溃。有时候,应用程序的按钮显示得非常糟糕。所以我决定自己开发一个简单实用的应用程序。

我们开始吧

在开始编写任何代码之前,我先声明:我跟你一样,也是 Flutter 新手。请务必自行研究。这一系列文章真的很棒。

掌握了 Flutter 和 Dart 的基础知识后,请继续阅读。

最有价值球员

我的应用及其首个版本,我希望拥有极简的用户界面和简单的功能。我决定采用以下规格:

  • 计时器时长从 5 分钟到 60 分钟不等。
  • 用户可以以 5 分钟为单位增加或减少时间。
  • 它有一个启动/停止按钮

这是结果

应用服务:Flutter 方法调用

这个应用有一个倒计时器。我需要一个服务来保持计时器运行,并在时间结束后关闭音乐。
但是,另一个应用的问题就出在这里:当用户关闭应用并重新打开时,需要知道服务中计时器已经运行了多长时间,这样才能更新应用状态并显示当前时间。

我之前就知道需要编写一个 Android 服务,所以我做了一些研究,找到了我需要的东西。Flutter方法调用文档

构建 Android 服务

首先,我们需要在 Android 清单文件(android/app/src/main/AndroidManifest.xml)中声明该服务。



<service android:enabled="true" android:exported="true" android:name="dev.protium.rest.AppService" />


Enter fullscreen mode Exit fullscreen mode

现在我们需要编写服务。该服务需要满足以下规范:

1)它应该能够与主活动建立连接。

为此,我们使用绑定器。



private final IBinder binder = new AppServiceBinder();
public class AppServiceBinder extends Binder {
        AppService getService() {
            return  AppService.this;
        }
    }

@Override
public IBinder onBind(Intent intent) {
     return binder;
}


Enter fullscreen mode Exit fullscreen mode

2) 它应该能够按需启动、停止并获取当前秒数

这段代码与本文无关,但您可以在文章末尾看到代码仓库。

3)如果有音乐正在播放,则应暂停音乐。

如果你想知道如何在安卓设备上暂停音乐,这就是诀窍



AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
if (am.isMusicActive()) {
  long eventtime = SystemClock.uptimeMillis();
  KeyEvent downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PAUSE, 0);
  am.dispatchMediaKeyEvent(downEvent);
  KeyEvent upEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PAUSE, 0);
  am.dispatchMediaKeyEvent(upEvent);
}


Enter fullscreen mode Exit fullscreen mode

神奇吧?

正在连接到服务

这时你可能已经注意到,沟通方式是这样的



Android Service <-> Android Activity <-> Flutter App


Enter fullscreen mode Exit fullscreen mode

从活动中连接

这很简单



private void connectToService() {
        if (!serviceConnected) {
            Intent service = new Intent(this, AppService.class);
            startService(service);
            bindService(service, connection, Context.BIND_AUTO_CREATE);
        } else {
            Log.i(TAG, "Service already connected");
            if (keepResult != null) {
                keepResult.success(null);
                keepResult = null;
            }
        }
    }

private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            AppService.AppServiceBinder binder = (AppService.AppServiceBinder) service;
            appService = binder.getService();
            serviceConnected = true;
            Log.i(TAG, "Service connected");
            if (keepResult != null) {
                keepResult.success(null);
                keepResult = null;
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            serviceConnected = false;
            Log.i(TAG, "Service disconnected");
        }
    };


Enter fullscreen mode Exit fullscreen mode

你注意到这个keepResult变量了吗?稍后会详细讨论。

将 Activity 连接到 Flutter



static final String CHANNEL = "dev.protium.rest/service";
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);

        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(this::onMethodCall);
    }


Enter fullscreen mode Exit fullscreen mode

我决定在 Activity 本身中实现该接口,因此您需要更改类声明。



public class MainActivity extends FlutterActivity implements MethodChannel.MethodCallHandler {


Enter fullscreen mode Exit fullscreen mode

现在是实施阶段。



@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
        try {
            if (call.method.equals("connect")) {
                connectToService();
                keepResult = result;
            } else if (serviceConnected) {
                if (call.method.equals("start")) {
                    appService.startTimer(call.argument("duration"));
                    result.success(null);
                } else if (call.method.equals("stop")) {
                    appService.stopTimer();
                    result.success(null);
                } else if (call.method.equals("getCurrentSeconds")) {
                    int sec = appService.getCurrentSeconds();
                    result.success(sec);
                }
            } else {
                result.error(null, "App not connected to service", null);
            }
        } catch (Exception e) {
            result.error(null, e.getMessage(), null);
        }
    }


Enter fullscreen mode Exit fullscreen mode

我们将它保存MethodChannel.Result在变量中keepResult。为什么呢?当我们将服务绑定到服务连接时,监听器会被onServiceConnected调用。这样我们就知道已经连接到服务了。Flutter 应用会等待这个回调函数成功或失败。别忘了调用它。

将 Flutter 连接到 Activity

首先,我们需要在 lib/main.dart 中导入包。



import 'dart:async'
import 'package:flutter/services.dart';


Enter fullscreen mode Exit fullscreen mode

在我们的状态控件中,我们需要这个



static const MethodChannel platform = 
MethodChannel('dev.protium.rest/service');

Future<void> connectToService() async {
    try {
      await platform.invokeMethod<void>('connect');
      print('Connected to service');
    } on Exception catch (e) {
      print(e.toString());
    }
}



Enter fullscreen mode Exit fullscreen mode

注意:通道方法名称都与MainActivity中的相同。听起来是不是很熟悉?如果您开发过Apache Cordova 插件,那么这应该对您来说非常熟悉。

现在我们已经完成了这三个组件的连接。Flutter 应用连接成功后,就可以调用启动/停止方法,或者从服务计时器中获取当前秒数。



final int serviceCurrentSeconds = await getServiceCurrentSeconds();
setState(() {
    _currentSeconds = serviceCurrentSeconds;
});


Enter fullscreen mode Exit fullscreen mode

任务完成。

我从 Flutter 中学到的东西

正如我之前提到的,我不喜欢“你好,世界”之类的应用。这个应用虽然很简单,但也用到了一些技巧。我觉得我爱上了 Flutter 和 Dart 的类型推断。整个过程只用了两天,我可不是在吹牛。昨天我坐在电脑前开始开发,晚上就完成了。今天我把它发布到了Google Play 商店,并写了这篇文章。(顺便说一句,这是我第一次发布应用。)

我学到了很多技巧,举几个例子:

  • 如何在不调用服务的情况下运行小部件测试
  • 如何通过先设置应用程序权限来运行试驾
  • 它有多重要、多有用?flutter analyze你可以在仓库中看到所有这些。

    GitHub 标志 protiumx /休息

    休息一段时间后关掉音乐,好好休息。

    休息

    注意:此仓库已停止维护。

    一个简单的应用程序,可以在一段时间后暂停音乐。该应用程序使用 Flutter 开发。

    从 Google Play 安装

    在 Google Play 上获取

    发展

    依赖关系:

    • Android SDK
    • Android Studio
    • Flutter SDK

    使用以下命令检查您的依赖项:flutter doctor

    测试

    flutter test
    flutter drive --target=test_driver/home_page.dart 
    

    跑步

    flutter run

    屏幕截图

    待办事项

    • 添加置顶通知
    • 音乐暂停时显示提示信息

    如果你喜欢这个应用,欢迎请我喝杯咖啡。

    请我喝杯咖啡

    © Brian Mayo - 2019



我还会发布更多文章,分享我从这个小项目中学到的其他技巧。我鼓励你从你的“百万级创意清单”中汲取灵感,用 Flutter 实现一个属于你自己的项目。

结论

我很高兴只花了几分钟就轻松地搭建出了一个漂亮的界面。我非常渴望体验Flutter Web 和桌面平台。我看到了它巨大的潜力,所以我告诉公司首席技术官,我们必须把一些产品迁移到 Flutter。这样做的好处显而易见,因为我们有跨平台应用(电视、智能手机、桌面电脑,任何支持 WebView 的设备)。

欢迎试用这款应用,也欢迎您贡献更多功能。我计划添加更多功能,但目前基本功能已经足够。
如果您想将其编译并发布到iOS 平台,请告诉我。

文章来源:https://dev.to/protium/flutter-background-services-19a4