美文网首页Flutter知识库Flutter学习
Flutter MethodChannel 原生通信导致的Rep

Flutter MethodChannel 原生通信导致的Rep

作者: 巴黎没有摩天轮Li | 来源:发表于2020-10-19 15:16 被阅读0次

    前言

    最近在做公司的Flutter项目,在封装扫码插件的时候,Bugly显示Reply already submitted问题,就此记录一下。

    2020-10-19 13:54:52.823 31754-31754/com.xxx.qr_code_plugin_example E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.xxx.qr_code_plugin_example, PID: 31754
        java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=65517, result=-1, data=Intent { (has extras) }} to activity {com.xxx.qr_code_plugin_example/com.xxx.qr_code_plugin_example.MainActivity}: java.lang.IllegalStateException: Reply already submitted
            at android.app.ActivityThread.deliverResults(ActivityThread.java:4938)
            at android.app.ActivityThread.handleSendResult(ActivityThread.java:4979)
            at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2044)
            at android.os.Handler.dispatchMessage(Handler.java:107)
            at android.os.Looper.loop(Looper.java:224)
            at android.app.ActivityThread.main(ActivityThread.java:7560)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
         Caused by: java.lang.IllegalStateException: Reply already submitted
            at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:139)
            at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:235)
            at com.xxx.qr_code_plugin.QrCodePlugin.onActivityResult(QrCodePlugin.kt:199)
            at io.flutter.embedding.engine.FlutterEnginePluginRegistry$FlutterEngineActivityPluginBinding.onActivityResult(FlutterEnginePluginRegistry.java:691)
            at io.flutter.embedding.engine.FlutterEnginePluginRegistry.onActivityResult(FlutterEnginePluginRegistry.java:378)
            at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onActivityResult(FlutterActivityAndFragmentDelegate.java:619)
            at io.flutter.embedding.android.FlutterActivity.onActivityResult(FlutterActivity.java:584)
            at android.app.Activity.dispatchActivityResult(Activity.java:8250)
            at android.app.ActivityThread.deliverResults(ActivityThread.java:4931)
            at android.app.ActivityThread.handleSendResult(ActivityThread.java:4979) 
            at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51) 
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2044) 
            at android.os.Handler.dispatchMessage(Handler.java:107) 
            at android.os.Looper.loop(Looper.java:224) 
            at android.app.ActivityThread.main(ActivityThread.java:7560) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) 
    

    问题复现

    override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
            mMethodResult = result
            mCallMethod = call 
            // 调用两次success方法
            result.success("xxx")
            result.success("xxx")
        }
    

    写法很多,网上有说是因为switch case语句没有设置default导致的该问题,但是设置了default确实可以解决,但是为什么会导致这个问题,原因不明。

    解决办法

    fun ignoreIllegalState(fn: () -> Unit) {
            try {
                fn()
            }catch (e:IllegalStateException){
                // ignore
            }
        }
    

    Result#success()调用流程

    MethodChannel

    private final class IncomingMethodCallHandler implements BinaryMessageHandler {
        private final MethodCallHandler handler;
    
        IncomingMethodCallHandler(MethodCallHandler handler) {
          this.handler = handler;
        }
    
        @Override
        @UiThread
        public void onMessage(ByteBuffer message, final BinaryReply reply) {
          final MethodCall call = codec.decodeMethodCall(message);
          try {
            handler.onMethodCall(
                call,
                new Result() {
                  @Override
                  public void success(Object result) {
                    // 执行 success() 方法
                    reply.reply(codec.encodeSuccessEnvelope(result));
                  }
    
                  @Override
                  public void error(String errorCode, String errorMessage, Object errorDetails) {
                    reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
                  }
    
                  @Override
                  public void notImplemented() {
                    reply.reply(null);
                  }
                });
          } catch (RuntimeException e) {
            Log.e(TAG + name, "Failed to handle method call", e);
            reply.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
          }
        }
      }
    

    由此看到,IncomingMethodCallHandler 实现了BinaryMessageHandler 接口,也就实现了接口方法onMessage(), 看下具体是谁调用了onMessage()方法。

    DartMessenger#handleMessageFromDart()

    @Override
    public void handleMessageFromDart(
        @NonNull final String channel, @Nullable byte[] message, final int replyId) {
      Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
      BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
      if (handler != null) {
        try {
          Log.v(TAG, "Deferring to registered handler to process message.");
          final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
          // 这里参数直接初始化Reply()
          handler.onMessage(buffer, new Reply(flutterJNI, replyId));
        } catch (Exception ex) {
          Log.e(TAG, "Uncaught exception in binary message listener", ex);
          flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
        }
      } else {
        Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message."
        flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
      }
    }
    

    最终调用的就是Reply的reply方法。

    private static class Reply implements BinaryMessenger.BinaryReply {
      @NonNull private final FlutterJNI flutterJNI;
      private final int replyId;
      // 1 
      private final AtomicBoolean done = new AtomicBoolean(false);
      Reply(@NonNull FlutterJNI flutterJNI, int replyId) {
        this.flutterJNI = flutterJNI;
        this.replyId = replyId;
      }
      @Override
      public void reply(@Nullable ByteBuffer reply) {
        // 2
        if (done.getAndSet(true)) {
          throw new IllegalStateException("Reply already submitted");
        }
        if (reply == null) {
          flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
        } else {
          flutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());
        }
      }
    }
    

    1处代码使用了CAS原子性变量,为了记录此次一次的Method Call 流程是否已完成。2处代码就是抛出异常Reply already submitted的原因。若流程第一次调用,则会走FlutterJNI,进行真正的通信过程,通过字节流进行数据的相互传递。

    所以解决问题办法,就是从代码逻辑判断是什么情况会导致同一个Result对象会调用多次的success()。

    相关文章

      网友评论

        本文标题:Flutter MethodChannel 原生通信导致的Rep

        本文链接:https://www.haomeiwen.com/subject/hppkmktx.html