API 30设备中印地语文本转语音(TTS)失效问题求助
解决API Level 30+设备上印地语TTS无法运行的问题
我之前也碰到过几乎一模一样的情况,API 30之后的包可见性限制加上Locale处理的小细节,很容易导致印地语TTS抛出"语言不支持"的错误(返回值-2)。咱们一步步来把这个问题搞定:
一、先补全包可见性配置
你已经添加了<intent>指向TTS_SERVICE,但API30+还需要额外配置来允许查询所有已安装的TTS引擎应用,否则你的App可能无法正确检测到系统的印地语语音包。更新你的Manifest配置:
<queries> <!-- 允许访问系统TTS服务 --> <intent> <action android:name="android.intent.action.TTS_SERVICE" /> </intent> <!-- 允许查询所有提供TTS服务的应用(API30+必需) --> <intent> <category android:name="android.intent.category.DEFAULT" /> <action android:name="android.speech.tts.engine.TTS_SERVICE" /> </intent> </queries>
如果需要兼容特定第三方TTS引擎(比如三星、谷歌的独立TTS应用),也可以直接指定包名:
<queries> <intent> <action android:name="android.intent.action.TTS_SERVICE" /> </intent> <!-- 谷歌文字转语音 --> <package android:name="com.google.android.tts" /> <!-- 三星TTS引擎 --> <package android:name="com.samsung.android.app.svoice" /> </queries>
二、优化Locale初始化与语言检测逻辑
你的代码里已经尝试了Locale.forLanguageTag("hin"),但可以更严谨一些:先检测系统支持的Locale,再选择正确的印地语Locale,同时添加语言包缺失时的引导逻辑。修改你的setTextTospeech方法:
private void setTextTospeech() { textToSpeech = new TextToSpeech(mContext, new TextToSpeech.OnInitListener() { @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { Locale targetLocale; // 区分英文和印地语Locale if (language.toLowerCase().contains(langaugeCodeEnglish)) { targetLocale = new Locale("en", "IN"); } else { // API30+推荐使用带区域的语言标签,兼容性更好 targetLocale = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Locale.forLanguageTag("hin-IN") : new Locale("hi", "IN"); } int result = textToSpeech.setLanguage(targetLocale); if (result == TextToSpeech.LANG_MISSING_DATA) { // 语言包缺失,引导用户跳转安装 Log.e("Text2SpeechWidget", "印地语语音包未安装,正在引导安装"); Intent installIntent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA); mContext.startActivity(installIntent); } else if (result == TextToSpeech.LANG_NOT_SUPPORTED) { // 尝试备用印地语Locale Log.e("Text2SpeechWidget", targetLocale + " 不支持,尝试通用印地语Locale"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { result = textToSpeech.setLanguage(Locale.forLanguageTag("hin")); if (result != TextToSpeech.SUCCESS) { Log.e("Text2SpeechWidget", "所有印地语Locale均不支持"); } } } else { Log.d("Text2SpeechWidget", targetLocale + " TTS配置成功"); } } else { Log.e("Text2SpeechWidget", "TTS初始化失败,状态码:" + status); } } }); }
三、调试与验证的关键步骤
- 检查系统TTS引擎状态:在设备的
设置 -> 辅助功能 -> 文字转语音输出里,确认默认TTS引擎(比如Google文字转语音)是否已经安装了印地语语音包。如果没有,手动安装后再测试。 - 打印支持的Locale列表:可以在TTS初始化成功后,打印所有支持的Locale,确认印地语是否在列表中:
if (status == TextToSpeech.SUCCESS) { Set<Locale> supportedLocales = textToSpeech.getAvailableLanguages(); for (Locale locale : supportedLocales) { Log.d("SupportedLocales", locale.toString()); } } - 完善speak方法的健壮性:你的speak方法已经做了API版本兼容,这里可以加上空指针判断,避免TTS未初始化时崩溃:
private void speak(String s, String text) { if (textToSpeech == null || textToSpeech.getEngines().isEmpty()) { Log.e("Text2SpeechWidget", "TTS未初始化或无可用引擎"); return; } try{ float pitch = (float) 0.62; float speed = (float) 0.86; textToSpeech.setSpeechRate(speed); textToSpeech.setPitch(pitch); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Bundle bundle = new Bundle(); bundle.putInt(TextToSpeech.Engine.KEY_PARAM_STREAM, AudioManager.STREAM_MUSIC); // 添加唯一标识,方便后续监听播放状态(可选) textToSpeech.speak(s, TextToSpeech.QUEUE_FLUSH, bundle, "speak_s_tag"); textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, bundle, "speak_text_tag"); } else { HashMap<String, String> param = new HashMap<>(); param.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_MUSIC)); textToSpeech.speak(s, TextToSpeech.QUEUE_FLUSH, param); textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, param); } }catch (Exception ae){ ae.printStackTrace(); } }
为什么API30+会出问题?
核心原因是Android 11(API30)引入了包可见性限制:默认情况下,你的App无法访问其他应用的信息,包括TTS引擎的语言支持情况。即使你加了TTS_SERVICE的intent查询,也可能无法获取完整的TTS引擎信息,导致系统误判印地语不支持。补全queries配置后,App才能正确检测到TTS引擎的语言包。
内容的提问来源于stack exchange,提问作者Ankur Shinde




