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
I think it is happening after Flutter upgrade > 1.5.4.hotfix.
Anyway, Yes there is a solution (Refer this github issue),
In your Activity
below 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);
//.....
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With