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你可以在仓库中看到所有这些。
休息
注意: 此仓库已停止维护。
一个简单的应用程序,可以在一段时间后暂停音乐。该应用程序使用 Flutter 开发。
从 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