You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用EventChannel向Flutter发送读卡数据报FlutterJNI detached错误如何解决

问题根源

报错本质是原生侧调用EventSink发送消息时,对应的FlutterJNI已经和原生层分离,所持有的EventSink已失效,核心触发原因有3个:

  • 原生ICardEventCallHandler的onCancel回调中未将存储的events对象置空,也没有在发送消息前做有效性校验,失效的sink不会被自动清理
  • Dart侧没有对EventChannel的订阅做生命周期绑定,页面销毁/APP退后台/Flutter引擎重启时未取消订阅,原生会一直持有失效的sink引用
  • 你大概率将ICardEventCallHandler作为全局单例使用,APP长时间在后台被系统回收Flutter引擎、或者二次启动时,旧的sink没有被新初始化的sink覆盖,直接调用就会触发报错

修复方案

1. 原生Java端修改

调整ICardEventCallHandler逻辑,增加空校验、异常捕获,取消监听时清空sink:

package com.test.handler.card;
import com.alibaba.fastjson.JSON;
import com.test.entity.ICard;
import io.flutter.plugin.common.EventChannel;

public class ICardEventCallHandler implements EventChannel.StreamHandler,EventSender{
    private Object arguments;
    private EventChannel.EventSink events;
    @Override
    public void onListen(Object arguments, EventChannel.EventSink events) {
        this.arguments = arguments;
        // 每次新的监听覆盖旧的sink,避免持有失效引用
        this.events = events;
    }

    @Override
    public void onCancel(Object arguments) {
        this.arguments = arguments;
        // 取消监听时清空sink
        this.events = null;
    }

    @Override
    public void sendCard(ICard iCard) {
        // 先判空,再加异常捕获,避免极端场景下sink刚好失效
        if (this.events != null) {
            try {
                String cardString = JSON.toJSONString(iCard);
                this.events.success(cardString);
            } catch (Exception e) {
                // 出现异常说明sink已失效,直接清空
                this.events = null;
            }
        }
    }
}

如果是多引擎场景,每次Flutter引擎初始化时要重新注册EventChannel,不要复用全局的ICardEventCallHandler实例。

2. Flutter Dart端修改

给EventChannel的订阅增加取消逻辑,和业务生命周期绑定:

import 'package:flutter/services.dart';
class ChannelHelper{
   static const eventChannel = const EventChannel("com.card/event");
   // 保存订阅实例,用于取消监听
   static StreamSubscription? _cardSubscription;

   static void handleReadCard(void onData(dynamic event)){
     // 先取消旧的订阅,避免重复监听
     cancelCardListener();
     LoggerPrinter.info("listening card.");
     _cardSubscription = eventChannel.receiveBroadcastStream().listen(onData);
   }

   // 提供取消监听的方法,在页面dispose、APP退后台不需要监听时调用
   static void cancelCardListener() {
     _cardSubscription?.cancel();
     _cardSubscription = null;
   }
}

如果是全局监听,可以在APP的initState中调用handleReadCard,在APP退到后台不需要读卡时调用cancelCardListener,回到前台时重新注册监听即可。


内容的提问来源于stack exchange,提问作者hehe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 15:15:00