当后退按钮按下销毁应用程序时,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:通过按下后退按钮解除键盘时,不关注文本字段(多行)

在颤振中,按下后退按钮时如何返回上一个 URL?

使用flutter webview作为主页并按下后退按钮关闭应用程序

按下设备后退按钮上的 Flutter WebView 异常

颤振:如何在按下后退按钮时不退出应用程序

按下后退按钮时保存