You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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);
            }
        }
    });
}

三、调试与验证的关键步骤

  1. 检查系统TTS引擎状态:在设备的设置 -> 辅助功能 -> 文字转语音输出里,确认默认TTS引擎(比如Google文字转语音)是否已经安装了印地语语音包。如果没有,手动安装后再测试。
  2. 打印支持的Locale列表:可以在TTS初始化成功后,打印所有支持的Locale,确认印地语是否在列表中:
    if (status == TextToSpeech.SUCCESS) {
        Set<Locale> supportedLocales = textToSpeech.getAvailableLanguages();
        for (Locale locale : supportedLocales) {
            Log.d("SupportedLocales", locale.toString());
        }
    }
    
  3. 完善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

火山引擎 最新活动