dlutton / flutter_tts

Flutter Text to Speech package
MIT License
619 stars 258 forks source link

App crashes at `com.tundralabs.fluttertts.FlutterTtsPlugin$4.onInit(FlutterTtsPlugin.java:110)` #289

Open dev-yakuza opened 2 years ago

dev-yakuza commented 2 years ago

🐛 Bug Report

First, thanks for making the awesome open-source library. 🙇‍♂️

I developed an app with flutter_tts and deployed it recently.

And I got the Crashlytics report like the below.

Fatal Exception: java.lang.IllegalStateException: Reply already submitted
       at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:35)
       at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:14)
       at com.tundralabs.fluttertts.FlutterTtsPlugin.onMethodCall(FlutterTtsPlugin.java:631)
       at com.tundralabs.fluttertts.FlutterTtsPlugin$5.run(FlutterTtsPlugin.java:6)
       at com.tundralabs.fluttertts.FlutterTtsPlugin$4.onInit(FlutterTtsPlugin.java:110)
       at android.speech.tts.TextToSpeech.dispatchOnInit(TextToSpeech.java:863)
       at android.speech.tts.TextToSpeech.access$800(TextToSpeech.java:77)
       at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2226)
       at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2181)
       at android.os.AsyncTask.finish(AsyncTask.java:771)
       at android.os.AsyncTask.access$900(AsyncTask.java:199)
       at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:246)
       at android.app.ActivityThread.main(ActivityThread.java:8653)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)

I saw the issue comment https://github.com/dlutton/flutter_tts/issues/261#issuecomment-904129032, so I added delay like the below.

TtsController.tts = FlutterTts();
await Future.delayed(const Duration(seconds: 1));
...
if (await tts.getDefaultEngine == Constants.googleTtsName) return;

final engines = await tts.getEngines;
if (engines.contains(Constants.googleTtsName)) {
  await tts.setEngine(Constants.googleTtsName);
  await Future.delayed(const Duration(seconds: 1));
} else {
  ...
}

However, I still get the report. The crash is on Galaxy S20 FE 5G and Android OS 11. 😭

Expected behavior

I hope the crash doesn't occur.

Reproduction steps

I can't. I have a Google Pixel4a, but it works perfectly.

Configuration

Version: 3.2.2

Platform:

dlutton commented 2 years ago

@dev-yakuza does this error only occur with setEngine?

dev-yakuza commented 2 years ago

does this error only occur with setEngine?

@dlutton I'm not sure. Yesterday, I got 3 reports.

Screen Shot 2021-12-10 at 7 47 57

Fatal Exception: java.lang.IllegalStateException: Reply already submitted
       at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:35)
       at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:14)
       at com.tundralabs.fluttertts.FlutterTtsPlugin.onMethodCall(FlutterTtsPlugin.java:631)
       at com.tundralabs.fluttertts.FlutterTtsPlugin$5.run(FlutterTtsPlugin.java:6)
       at com.tundralabs.fluttertts.FlutterTtsPlugin$4.onInit(FlutterTtsPlugin.java:110)
       at android.speech.tts.TextToSpeech.dispatchOnInit(TextToSpeech.java:863)
       at android.speech.tts.TextToSpeech.access$800(TextToSpeech.java:77)
       at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2226)
       at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2181)
       at android.os.AsyncTask.finish(AsyncTask.java:771)
       at android.os.AsyncTask.access$900(AsyncTask.java:199)
       at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:246)
       at android.app.ActivityThread.main(ActivityThread.java:8633)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Fatal Exception: java.lang.IllegalStateException: Reply already submitted
       at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:35)
       at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:14)
       at com.tundralabs.fluttertts.FlutterTtsPlugin.getDefaultEngine(FlutterTtsPlugin.java:6)
       at com.tundralabs.fluttertts.FlutterTtsPlugin.onMethodCall(FlutterTtsPlugin.java:583)
       at com.tundralabs.fluttertts.FlutterTtsPlugin$5.run(FlutterTtsPlugin.java:6)
       at com.tundralabs.fluttertts.FlutterTtsPlugin$4.onInit(FlutterTtsPlugin.java:110)
       at android.speech.tts.TextToSpeech.dispatchOnInit(TextToSpeech.java:835)
       at android.speech.tts.TextToSpeech.access$1500(TextToSpeech.java:63)
       at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2226)
       at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2181)
       at android.os.AsyncTask.finish(AsyncTask.java:755)
       at android.os.AsyncTask.access$900(AsyncTask.java:192)
       at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:772)
       at android.os.Handler.dispatchMessage(Handler.java:107)
       at android.os.Looper.loop(Looper.java:237)
       at android.app.ActivityThread.main(ActivityThread.java:8107)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)
Fatal Exception: java.lang.IllegalStateException: Reply already submitted
       at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:35)
       at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:14)
       at com.tundralabs.fluttertts.FlutterTtsPlugin.onMethodCall(FlutterTtsPlugin.java:631)
       at com.tundralabs.fluttertts.FlutterTtsPlugin$5.run(FlutterTtsPlugin.java:6)
       at com.tundralabs.fluttertts.FlutterTtsPlugin$4.onInit(FlutterTtsPlugin.java:110)
       at android.speech.tts.TextToSpeech.dispatchOnInit(TextToSpeech.java:863)
       at android.speech.tts.TextToSpeech.access$800(TextToSpeech.java:77)
       at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2226)
       at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2181)
       at android.os.AsyncTask.finish(AsyncTask.java:771)
       at android.os.AsyncTask.access$900(AsyncTask.java:199)
       at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:246)
       at android.app.ActivityThread.main(ActivityThread.java:8653)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
dev-yakuza commented 2 years ago

@dlutton I service the app for Korean. 🇰🇷 And Galaxy in Korea doesn't have GoogleTTS initially. 😭

So, I have checking the engine logic like below.

if (await tts.getDefaultEngine == Constants.googleTtsName) return;

final engines = await tts.getEngines;
if (engines.contains(Constants.googleTtsName)) {
  await tts.setEngine(Constants.googleTtsName);
  await Future.delayed(const Duration(seconds: 1));
} else {
  Get.dialog(
     // Alert with download button of Play store
  );
}

Today, I will try to remove the logic and test it to make sure the crashes occur in setEngine. 🔬

(Question) I saw https://github.com/dlutton/flutter_tts/issues/261#issuecomment-904000289. If there is no Google TTS, Is there no problem? 🤔

dlutton commented 2 years ago

@dev-yakuza Google TTS is the default, however if you're trying to set the engine and receiving that error, it could be an issue with setEngine. I'll look into that.

dev-yakuza commented 2 years ago

@dlutton

Today, I will try to remove the logic and test it to make sure the crashes occur in setEngine. 🔬

I tested it. And there are no crash reports, So getDefaultEngine, getEngines, or setEngine makes the app crash.

I hope this info is helpful to debug it 🙏

dlutton commented 2 years ago

@dev-yakuza that's extremely helpful. I'll use that to pinpoint the issues with those methods on Android. Thank you

TranTuanManh commented 2 years ago

@dlutton I got same issue like below:

Exception java.lang.IllegalStateException: 
  at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply (DartMessenger.java)
  at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success (MethodChannel.java)
  at com.tundralabs.fluttertts.FlutterTtsPlugin.getDefaultEngine (FlutterTtsPlugin.java)
  at com.tundralabs.fluttertts.FlutterTtsPlugin.onMethodCall (FlutterTtsPlugin.java)
  at com.tundralabs.fluttertts.FlutterTtsPlugin$5.run (FlutterTtsPlugin.java)
  at com.tundralabs.fluttertts.FlutterTtsPlugin$4.onInit (FlutterTtsPlugin.java)
  at android.speech.tts.TextToSpeech.dispatchOnInit (TextToSpeech.java:863)
  at android.speech.tts.TextToSpeech.access$800 (TextToSpeech.java:77)
  at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute (TextToSpeech.java:2226)
  at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute (TextToSpeech.java:2181)
  at android.os.AsyncTask.finish (AsyncTask.java:771)
  at android.os.AsyncTask.access$900 (AsyncTask.java:199)
  at android.os.AsyncTask$InternalHandler.handleMessage (AsyncTask.java:788)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:246)
  at android.app.ActivityThread.main (ActivityThread.java:8512)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)

This error sometimes occurs not always. I found that they only occur on Samsung devices. The versions I'm using are 3.2.2 and 3.5.0

dlutton commented 2 years ago

@TranTuanManh are you also only receiving these errors with getDefaultEngine, getEngines, or setEngine?

TranTuanManh commented 2 years ago

@dlutton Yes. I hope you can fix it soon. Thank you for the awesome lib.

chimura commented 1 year ago

I've got the same issue when submitting an Android App to Google Play Console, pre-launch report.

It tested on 3 different devices samsung SM-G981U1 Android 10 (SDK 29), google Pixel 6 Android 12 (SDK 31) and google Redfin 64-bit Android 13 (SDK 33) only

Same error for all three:

java.lang.IllegalStateException: Reply already submitted
    at w2.c$g.a(Unknown Source:35)
    at j3.k$a$a.a(Unknown Source:14)
    at u2.g.B(Unknown Source:14)
    at u2.g.i(Unknown Source:576)
    at u2.g.P(Unknown Source:15)
    at u2.g.g(Unknown Source:0)
    at u2.e.run(Unknown Source:6)
    at u2.g.O(Unknown Source:95)
    at u2.g.c(Unknown Source:0)
    at u2.a.onInit(Unknown Source:2)
    at android.speech.tts.TextToSpeech.lambda$dispatchOnInit$0$TextToSpeech(TextToSpeech.java:890)
    at android.speech.tts.TextToSpeech$$ExternalSyntheticLambda17.run(Unknown Source:4)
    at android.speech.tts.TextToSpeech.dispatchOnInit(TextToSpeech.java:899)
    at android.speech.tts.TextToSpeech.access$900(TextToSpeech.java:79)
    at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2280)
    at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2240)
    at android.os.AsyncTask.finish(AsyncTask.java:771)
    at android.os.AsyncTask.access$900(AsyncTask.java:199)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7839)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

Does anybody know a workaround to this issue?

dlutton commented 1 year ago

@chimura do you know which method is being invoked when this error appears?

chimura commented 1 year ago

@dlutton Yeah, setEngine with the string for the default android tts as argument.

dlutton commented 1 year ago

thank you @chimura

hioshih commented 1 year ago

I am having the same issue with version 3.6.3. I had an old project with v3.5.1 that was working fine.

Weird thing is after I run flutter clean and downloaded packages again, everything is the same but now v3.5.1 crashes too. It only had the flutter_tts as dependencies.

It seems to crash when we try to set the same engine that is already being used.

For now I did something like this, and it is working:

FlutterTts _tts=FlutterTts();
String engine = '';
bool get isAndroid => !kIsWeb && Platform.isAndroid;

//call this instead of _tts.setEngine
Future setEngine(String newEngine) async {
    if (isAndroid) {
      //set on first call
      if (engine.isEmpty) {
        engine = await _tts.getDefaultEngine;
        //just in case there is only 1 engine and is not google (getDefaultEngine)
        var engines = await _tts.getEngines;
        if (engines.length == 1 && engines[0] != engine) {
          engine = engines[0];
        }
      }
      //change if necessary
      if (engine != newEngine) {
        await _tts.setEngine(newEngine);
        engine = newEngine;
      }
    }
  }