android - 当后退按钮按下销毁应用程序时,Flutter Android Platform EventChannel 不会停止
问题描述
我正在尝试创建一个颤振应用程序,它使用我正在创建的本机 android 插件的后台服务:一个简单的计数器应用程序,它每 5 秒通过 EventChannel 发送一次计数值。
当我按下后退按钮并且应用程序被销毁时,服务仍在运行(这就是我想要做的)。但是当我再次打开应用程序时,事件接收器不再将数据发送到新的颤动视图。我认为事件通道插件并没有停止。
解决方案
当我们按下后退按钮时,我们的应用程序就会被破坏。因此 EventSink(位于我们的 Native Code 中)无法将数据发送到我们的 Flutter 端。
您可以在控制台中看到此消息:尝试向 Flutter 发送平台消息,但 FlutterJNI 已与原生 C++ 分离。无法发送。通道:com.example.timestamp_saver 响应 ID:0 这里 com.example.timestamp_saver 是事件通道名称。
因此,要在按下返回按钮后再次打开应用程序时再次开始收听事件,您需要再次开始收听该频道,当您的应用程序状态处于“恢复”或按下某个按钮时,您可以执行此操作。
要根据应用程序状态执行此操作,请按照以下步骤操作 -
所以首先用WidgetsBindingObserver实现你的有状态小部件
然后在 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 发出的数据进行更新,因此在这种情况下,我将数据保存到数据库和当用户再次打开应用程序时,我正盯着同一个频道再次收听。
主要.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();
}
}
我的服务.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();
}
}
推荐阅读
- javascript - 运行存储在对象中的函数
- webgl - 将单个纹理的子集渲染到 WebGL 画布上的最快方法是什么?
- time - 如何在时间 x 后改变形状的颜色?
- ios - Xcode 应用程序
- latex - 如何不平衡乳胶中的书目列?
- c - 假设 realloc()ing 到较小的大小总是会成功,这是否安全?
- reactjs - Firebase 数据未保存到我的反应状态
- python - 使用 psutil 的 Python 程序在运行 8 小时后出现 PermissionError 失败:[WinError 5] 访问被拒绝 - 为什么?
- c# - _mm_set1_epi32 对应的 netCore SSE2 是什么
- android-studio - 您可以将 Python 脚本链接到 Android Studio 吗?