当后退按钮按下销毁应用程序时,Flutter Android Platform EventChannel 不会停止
Posted
技术标签:
【中文标题】当后退按钮按下销毁应用程序时,Flutter Android Platform EventChannel 不会停止【英文标题】:Flutter Android Platform EventChannel not stopping when back button press destroy application 【发布时间】:2020-01-06 11:56:27 【问题描述】:我正在尝试创建一个颤振应用程序,它使用我正在创建的本机 android 插件的后台服务:一个简单的计数器应用程序,它每 5 秒通过 EventChannel 发送一次计数值。
当我按下后退按钮并且应用程序被销毁时,服务仍在运行(这就是我想要做的)。但是当我再次打开应用程序时,事件接收器不再将数据发送到新的颤动视图。 我认为事件通道插件没有停止。
【问题讨论】:
【参考方案1】:当我们按下后退按钮时,我们的应用程序会被破坏。因此 EventSink(位于我们的 Native Code 中)无法将数据发送到我们的 Flutter 端。
您可以在控制台中看到此消息: 尝试向 Flutter 发送平台消息,但 FlutterJNI 与原生 C++ 分离。无法发送。频道:com.example.timestamp_saver 响应 ID:0 这里 com.example.timestamp_saver 是事件通道名称。
因此,当您按下返回按钮后再次打开应用程序时,要再次开始收听事件,您需要再次开始收听该频道,并且您可以在您的应用程序状态为“恢复”时执行此操作或 按下某个按钮。
要根据应用状态执行此操作,请按照以下步骤操作 -
所以首先使用 WidgetsBindingObserver
实现您的 Stateful 小部件然后在 initState() 方法和 dispose() 中
@override
void initState()
super.initState();
WidgetsBinding.instance.addObserver(this);
@override
void dispose()
WidgetsBinding.instance.removeObserver(this);
super.dispose();
现在覆盖 didChangeAppLifecycleState(AppLifecycleState state) 并在 Flutter 中获取应用程序的当前状态。 或 您也可以在按下一些刷新按钮时开始收听事件接收器
@override
void didChangeAppLifecycleState(AppLifecycleState state)
print('state = $state');
if(state == AppLifecycleState.resumed)
//start listening to the Event Sink again
listenData();
这里是listenData()方法-
void listenData() async
if(Platform.isAndroid)
var eventChannel = EventChannel("com.example.timestamp_saver");
eventChannel.receiveBroadcastStream("Just Listen").listen((event) async
debugPrint("Inside Listen Data");
debugPrint(event);
);
-
这是我的示例代码,我每 5 秒更新一次带有时间戳的列表。在这种情况下,当用户按下返回按钮时,侦听器将使用 EventSink 发出的数据进行更新,因此在这种情况下,我正在保存数据到数据库,当用户再次打开应用程序时,我正盯着同一个频道再次收听。
main.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:timestamp_saver/db/db_helper.dart';
import 'package:timestamp_saver/db/timestamp_model.dart';
void main()
runApp(MyApp());
class MyApp extends StatelessWidget
// This widget is the root of your application.
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
class MyHomePage extends StatefulWidget
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver
DBHelper _dbHelper;
List<TimeStampModel> timeStampList;
@override
void initState()
super.initState();
WidgetsBinding.instance.addObserver(this);
_dbHelper = DBHelper();
@override
void dispose()
WidgetsBinding.instance.removeObserver(this);
super.dispose();
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(),
body: Container(
child: Column(
children: [
RaisedButton(
onPressed: ()startServiceAndReceiveUpdatea();,
child: Text("Start"),
),
SizedBox(height: 20,),
RaisedButton(
onPressed: ()listenData();,
child: Text("Listen"),
),
Expanded(
child: FutureBuilder<List<TimeStampModel>>(
future: _dbHelper.getTimeStampModels(),
builder: (context, snapshot)
if(snapshot.hasData)
timeStampList = snapshot.data;
return ListView.builder(
itemCount: timeStampList.length,
itemBuilder: (context, index) => ListTile(
title: Text(timeStampList[index].timestamp),
),
);
else if(snapshot.hasError)
return Center(child: Text("Error"),);
else
return Center(child: CircularProgressIndicator(),);
,
)
)
],
),
)
);
void startServiceAndReceiveUpdatea() async
if(Platform.isAndroid)
var eventChannel = EventChannel("com.example.timestamp_saver");
eventChannel.receiveBroadcastStream("startService").listen((event) async
debugPrint("Listner called in dart");
debugPrint(event);
TimeStampModel timeStamp = TimeStampModel(id: null,timestamp: event);
setState(()
);
);
void listenData() async
if(Platform.isAndroid)
var eventChannel = EventChannel("com.example.timestamp_saver");
eventChannel.receiveBroadcastStream("Just Listen").listen((event) async
debugPrint("Inside Listen Data");
debugPrint(event);
TimeStampModel timeStamp = TimeStampModel(id: null,timestamp: event);
setState(()
);
);
MainActivity.java
package com.example.timestamp_saver;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Observer;
import java.nio.ByteBuffer;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity
private Intent forService;
static EventChannel.EventSink events;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine)
super.configureFlutterEngine(flutterEngine);
GeneratedPluginRegistrant.registerWith(flutterEngine);
forService = new Intent(MainActivity.this,MyService.class);
new EventChannel(flutterEngine.getDartExecutor(),"com.example.timestamp_saver").setStreamHandler(new EventChannel.StreamHandler()
@Override
public void onListen(Object arguments, EventChannel.EventSink events)
MainActivity.events = events;
System.out.println("On Listen in Activity called");
if(arguments.equals("startService"))
System.out.println("if");
startService();
else if(arguments.equals("Just Listen"))
System.out.println("Just Listen");
@Override
public void onCancel(Object arguments)
);
private void startService()
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForegroundService(forService);
else
startService(forService);
@Override
protected void onDestroy()
super.onDestroy();
MyService.java
package com.example.timestamp_saver;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.lifecycle.LifecycleService;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import io.flutter.plugin.common.EventChannel;
public class MyService extends LifecycleService
MutableLiveData<String> stringMutableLiveData = new MutableLiveData<>();
int timerCount = 0;
private PowerManager.WakeLock wl;
private DBManager dbManager;
@Override
public void onCreate()
super.onCreate();
dbManager = new DBManager(this);
dbManager.open();
//Creating a partial wake lock,Because if we don't create wake lock then CPU Will stop working when
//screen will be locked,which will cause timer to work inconsistently.
//This solution can be modified more.For example Acquire the Wake Lock when user locks the screen and release
//it when phone gets unlock.This can be achieved using Brodcast Receiver.
//Because Wake Lock consumes much power,so use it properly.
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"a:waketag");
wl.acquire();
//stringMutableLiveData = new MutableLiveData<>();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
NotificationCompat.Builder builder = new NotificationCompat.Builder(this,"messages")
.setContentText("This is running in Background")
.setContentTitle("Flutter Background")
.setSmallIcon(R.drawable.launch_background);
startForeground(101,builder.build());
timerLogic();
@Override
public int onStartCommand(Intent intent, int flags, int startId)
return super.onStartCommand(intent, flags, startId);
void timerLogic()
stringMutableLiveData.observe(this, new Observer<String>()
@Override
public void onChanged(String s)
System.out.println("On Changed");
if(MainActivity.events != null)
dbManager.insert(s);
MainActivity.events.success(s);
else
System.out.println("Event is null");
);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask()
@Override
public void run()
timerCount++;
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
String timerString = dateFormat.format(cal.getTime());
System.out.println(timerString+": "+timerCount);
stringMutableLiveData.postValue(timerString);
,1000,5000);
@Override
public void onDestroy()
//Remember to relese wake lock
wl.release();
super.onDestroy();
【讨论】:
以上是关于当后退按钮按下销毁应用程序时,Flutter Android Platform EventChannel 不会停止的主要内容,如果未能解决你的问题,请参考以下文章
Flutter:通过按下后退按钮解除键盘时,不关注文本字段(多行)