Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an solution for java.lang.IllegalStateException: Reply already submitted

Tags:

flutter

dart

I want to use pusher sdk in Flutter from android native code because its library no yet completely supported in flutter but when i send first message it received it successfully the next message make app crush with Reply already submitted error her on this line result.success(txt);

 public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "demo.gawkat.com/info";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);

        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler((methodCall, result) -> {

            final Map<String, Object> arguments = methodCall.arguments();

            if (methodCall.method.equals("getMessage")) {
                Pusher pusher = new Pusher("faa685e4bb3003eb825c");
                pusher.connect();
                Channel channel = pusher.subscribe("messages");

                channel.bind("new_message", (channelName, eventName, data) -> runOnUiThread(() -> {
                    Gson gson = new Gson();
                    Message message = gson.fromJson(data, Message.class);
                    String txt = message.text;
                    result.success(txt);


                }));


            }

        });
      }
    }

Flutter code:

Future<String> _getMessage() async {

    String value;

    try {
      value = await platform.invokeMethod('getMessage');
    } catch (e) {
      print(e);
    }

    return value;
 }

Error is

FATAL EXCEPTION: main
    Process: com.example.flutter_app, PID: 6296
    java.lang.IllegalStateException: Reply already submitted
        at io.flutter.view.FlutterNativeView$PlatformMessageHandlerImpl$1.reply(FlutterNativeView.java:197)
        at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:204)
        at com.example.flutter_app.MainActivity.lambda$null$0(MainActivity.java:40)
        at com.example.flutter_app.-$$Lambda$MainActivity$axbDTe2B0rhavWD22s4E8-fuCaQ.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:789)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767
like image 932
Hatem R Saber Avatar asked Apr 25 '19 09:04

Hatem R Saber


2 Answers

I think it is happening after Flutter upgrade > 1.5.4.hotfix.

Anyway, Yes there is a solution (Refer this github issue),

In your Activitybelow onCreate() add this class:

private static class MethodResultWrapper implements MethodChannel.Result {
    private MethodChannel.Result methodResult;
    private Handler handler;

    MethodResultWrapper(MethodChannel.Result result) {
        methodResult = result;
        handler = new Handler(Looper.getMainLooper());
    }

    @Override
    public void success(final Object result) {
        handler.post(
                new Runnable() {
                    @Override
                    public void run() {
                        methodResult.success(result);
                    }
                });
    }

    @Override
    public void error(
            final String errorCode, final String errorMessage, final Object errorDetails) {
        handler.post(
                new Runnable() {
                    @Override
                    public void run() {
                        methodResult.error(errorCode, errorMessage, errorDetails);
                    }
                });
    }

    @Override
    public void notImplemented() {
        handler.post(
                new Runnable() {
                    @Override
                    public void run() {
                        methodResult.notImplemented();
                    }
                });
    }
}

Then, instead of using MethodChannel result to setMethodCallHandler argument callback add name as rawResult and then inside that callback, add this line:

MethodChannel.Result result = new MethodResultWrapper(rawResult);

As below:

    //......
    new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
            (call, rawResult) -> {
                MethodChannel.Result result = new MethodResultWrapper(rawResult);
    //.....
like image 178
Blasanka Avatar answered Sep 29 '22 16:09

Blasanka


I use flags for this problem. Just make sure that methods of same channels are called simultaneously. The problem seem to appear then.

If two methods needs to be called simulatenously without any problem define both methods in 2 different channels

var resultMap = Map<String, MethodChannel.Result> = HashMap()

new MethodChannel(getFlutterView(), CHANNEL_1).setMethodCallHandler((methodCall, result) -> {

            final Map<String, Object> arguments = methodCall.arguments();

            if (methodCall.method.equals("method1")) {
                //  implement method 1
            }

        });

new MethodChannel(getFlutterView(), CHANNEL_2).setMethodCallHandler((methodCall, result) -> {

            final Map<String, Object> arguments = methodCall.arguments();

            if (methodCall.method.equals("method2")) {
                 resultMap = resultMap + mapOf(CHANNEL_2 to MethodResultWrapper(result) // use this later to return result
                 // implement method2
                 result.success(true) // or whatever value
             }

        });

This reduce the chance of "Reply already submitted" error.

Incase if you are using MethodResultWrapper as @Blasanka answer use flags before result.success

when method is invoked set flag to true

val methodCheckFlag: Boolean = true

then when result need to be returned

if(methodCheckFlag) {
   methodCheckFlag = false;
   methodWrapperResult?.success(true) // or what ever value to return
}

or use the saved MethodResultWrapper as

if(methodCheckFlag) {
       methodCheckFlag = false;
       resultMap[CHANNEL_2]?.success(true) // or what ever value to return
    }
like image 44
Jerin Avatar answered Sep 29 '22 16:09

Jerin