绕过Android中的Google TTS Engine初始化延迟

Sou*_*der 15 android android-service google-text-to-speech

我尝试在手机中触发特定事件时播放TextToSpeech对象.

但是,我遇到了大多数手机上安装的默认Google TTS引擎的问题.截至目前,我正在初始化TextToSpeech对象后立即播放一些文本,并在语音完成后立即关闭资源,如下面的代码所示:

public class VoiceGenerator {
private Context context = null;

private static TextToSpeech voice = null;

public VoiceGenerator(Context context)
{
    this.context = context;
}


public void voiceInit(String text)
{
    try {
        if (voice == null) {

            new Thread(new Runnable() {
                @Override
                public void run() {
                    voice = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
                        @Override
                        public void onInit(final int status) {
                            try {
                                if (status != TextToSpeech.ERROR) {
                                    voice.setLanguage(Locale.US);
                                    Log.d("VoiceTTS", "TTS being initialized");
                                    HashMap p = new HashMap<String, String>();
                                    p.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "ThisUtterance");

 //Speaking here
                           voice.speak(text, TextToSpeech.QUEUE_ADD, p);

                                    voice.setOnUtteranceProgressListener(new UtteranceProgressListener() {
                                        @Override
                                        public void onStart(String utteranceId) {

                                        }

                                        @Override
                                        public void onDone(String utteranceId) {
                                            Log.d("VoiceTTS", "TTS being released");
                                            clearTtsEngine();
                                        }

                                        @Override
                                        public void onError(String utteranceId) {

                                        }
                                    });
                                }

                            } catch (Exception e) {
                                clearTtsEngine();
                                Log.d("ErrorLog", "Error occurred while voice play");
                                e.printStackTrace();
                            }


                        }
                    });
                }
            }).start();

        }
    }
    catch(Exception e)
    {
        clearTtsEngine();
        Log.d("ErrorLog","Error occurred while voice play");
        e.printStackTrace();
    }
}

public static void clearTtsEngine()
{
    if(voice!=null)
    {
        voice.stop();
        voice.shutdown();
        voice = null;
    }



 }
}
Run Code Online (Sandbox Code Playgroud)

但是,我面临的问题是与初始化Google TTS引擎相关的延迟时间有限 - 我的设备大约需要6-8秒.

我已经读过其他帖子,通过使用其他TTS引擎可以避免这种延迟.由于我总是在我的三星手机上开发,默认情况下配置了自己专有的TTS,直到我在其他品牌手机上检查我的应用程序之前我才注意到这个问题,其中谷歌TTS引擎配置为默认值.但是,我理想情况下不想强迫用户与我自己一起安装另一个应用程序,因此我希望这可以使用默认的Google TTS引擎本身.

通过我稍后纠正的一些错误编码,我意识到如果我可以保持TextToSpeech对象事先初始化并且始终不为null - 一旦初始化,我似乎可以绕过这个延迟.

但是,由于有必要在完成后关闭资源,我无法保持对象存活并初始化很长时间,而且我不知道何时初始化/关闭资源,因为我在技术上需要在特定事件发生的任何时候播放语音,这主要是当我的应用程序未在手机上打开时.

所以我的问题如下:

  1. 我们可以通过编程方式或其他方式以某种方式减少或消除Google TTS Engine的初始化延迟吗?

  2. 有没有什么方法可以保持TextToSpeech对象在所有时间保持活动和初始化,比如通过服务?或者这会是一个糟糕的,耗费资源的设计?

  3. 对于我的要求,还有正确的方法使用静态 TextToSpeech对象吗?

任何解决方案以及代码将不胜感激.

更新:我已经确认延迟与谷歌TTS引擎完全相关,因为我尝试使用其他免费和付费的TTS引擎,其中很少或没有延迟.但是如果可能的话,我仍然希望没有任何第三方依赖项,并希望使用Google TTS Engine.

更新:我似乎通过将此TTS对象绑定到服务并从服务访问它来绕过此问题.该服务是STICKY(如果服务由于内存问题而终止,Android OS将在内存再次可用时重新启动服务)并配置为在重启设备时重新启动.

该服务仅初始化TTS对象,不执行任何其他工作.我没有明确停止服务,允许它尽可能长时间运行.我已将TTS对象定义为静态,因此我可以从我的应用程序的其他类访问它.

虽然这看起来效果非常好,但我担心这是否会导致内存或电池问题(在我的特定情况下,服务只处理对象初始化然后保持休眠状态).我的设计有任何问题,或者我的设计可以进行任何进一步的改进/检查吗?

清单文件:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>


<application
    android:allowBackup="false"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:name="activity.MainActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <receiver
        android:name="services.BroadcastReceiverOnBootComplete"
        android:enabled="true"
        android:exported="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.PACKAGE_REPLACED" />
            <data android:scheme="package" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.PACKAGE_ADDED" />
            <data android:scheme="package" />
        </intent-filter>
    </receiver>


    <service android:name="services.TTSService"></service>
Run Code Online (Sandbox Code Playgroud)

BroadcastReceiver代码:

public class BroadcastReceiverOnBootComplete extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {
        Intent serviceIntent = new Intent(context, TTSService.class);
        context.startService(serviceIntent);
    }
}
Run Code Online (Sandbox Code Playgroud)

}

TTSService代码:

public class TTSService extends Service {

private static TextToSpeech voice =null;

public static TextToSpeech getVoice() {
    return voice;
}

@Nullable
@Override

public IBinder onBind(Intent intent) {
    // not supporting binding
    return null;
}

public TTSService() {
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    try{
        Log.d("TTSService","Text-to-speech object initializing");

        voice = new TextToSpeech(TTSService.this,new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(final int status) {
                Log.d("TTSService","Text-to-speech object initialization complete");                   

            }
            });

    }
    catch(Exception e){
        e.printStackTrace();
    }


    return Service.START_STICKY;
}

@Override
public void onDestroy() {
    clearTtsEngine();
    super.onDestroy();

}

public static void clearTtsEngine()
{
    if(voice!=null)
    {
        voice.stop();
        voice.shutdown();
        voice = null;
    }



}
}
Run Code Online (Sandbox Code Playgroud)

修改后的VoiceGenerator代码:

public class VoiceGenerator {

private TextToSpeech voice = null;

public VoiceGenerator(Context context)
{
    this.context = context;
}


public void voiceInit(String text)
{
   try {
        if (voice == null) {

            new Thread(new Runnable() {
                @Override
                public void run() {

                    voice = TTSService.getVoice();
                    if(voice==null)
                        return;

                    voice.setLanguage(Locale.US);
                    HashMap p = new HashMap<String, String>();
                    p.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "ThisUtterance");
                    voice.speak(text, TextToSpeech.QUEUE_ADD, p);

                    voice.setOnUtteranceProgressListener(new UtteranceProgressListener() {
                        @Override
                        public void onStart(String utteranceId) {

                        }

                        @Override
                        public void onDone(String utteranceId) {
                        }

                        @Override
                        public void onError(String utteranceId) {

                        }
                    });
                }
            }).start();

        }
    }
    catch(Exception e)
    {
        Log.d("ErrorLog","Error occurred while voice play");
        e.printStackTrace();
    }
}




}
Run Code Online (Sandbox Code Playgroud)

bra*_*all 11

我是Android应用程序Saiy的开发者.这不是一个无耻的插件,它是为了证明我使用你正在考虑的设计模式,而且我已经"完成了"提示你的问题.

这是我的新鲜事,因为我去年重写了我的代码,并且必须充分考虑周围的问题.

  • 我们可以通过编程方式或其他方式以某种方式减少或消除Google TTS Engine的初始化延迟吗?

前段时间问了一个类似的问题,并在后台线程中初始化Text to Speech对象,它没有与其他任务竞争,可以稍微减少延迟(因为我看到你已经在你发布的代码中做了).

您还可以通过选择嵌入式语音而不是依赖于网络的语音来确保发言请求不会进一步延迟:

在API 21+中,查看Voice类的选项.特别是getFeatures(),您可以在其中检查网络的延迟和要求.

在API <21 - 在参数内将KEY_FEATURE_NETWORK_SYNTHESIS设置为false.

无论上述情况如何,谷歌TTS引擎的初始化时间都是我测试的任何引擎(我认为都是这些引擎).我相信这只是因为他们正在使用设备上的所有可用资源来提供最高质量的语音.

根据我自己的个人测试,此延迟与设备的硬件成正比.RAM越多,处理器性能越高,初始化时间越短.对于设备的当前状态也是如此 - 我想你会发现在重启后,有空闲内存并且Android不需要杀死其他进程,初始化时间将减少.

总之,除了上面提到的,,你不能减少初始化时间.

  • 有没有什么方法可以保持TextToSpeech对象在所有时间保持活动和初始化,比如通过服务?或者这会是一个糟糕的,耗费资源的设计?

  • 对于我的要求,还有正确的方法使用静态TextToSpeech对象吗?

正如您所指出的,避免初始化时间的一种方法是继续绑定引擎.但是,在执行此操作之前,您可能还需要考虑其他问题.

如果设备处于需要释放资源的状态,这是导致扩展初始化延迟的相同状态,则Android完全有权进行垃圾收集此绑定.如果您在后台服务中持有此绑定,则可以终止该服务,使您回到原点.

此外,如果您仍然与引擎绑定,您的用户将在Android运行的应用程序设置中看到集体内存使用情况.对于许多不正确地考虑(休眠)内存使用量的用户而言,与电池消耗成正比,根据我的经验,这将导致卸载和应用程序评级不佳.

在撰写本文时,Google TTS将以70mb的成本绑定到我的应用程序.

如果您仍希望在此基础上继续,您可以尝试让Android优先处理您的流程并最后终止它 - 您可以使用Foreground Service执行此操作.这打开了另一种蠕虫病毒,我不打算这样做.

实际上,绑定到服务中的引擎并在您希望引擎发言时检查该服务是否正在运行,这是一种"单例模式".在这项服务中使引擎静止不会有任何我能想到的目的.

您可以在这里看到我如何开始处理TTS初始化以及可能发生的相关问题 - 包括滞后.

最后,分享一下我如何处理上述问题的经验.

我在应用程序中的"已知错误"和"常见问题解答"的顶部" 谷歌很难初始化 ".

我监控引擎呼叫所需的时间onInit.如果花了太长时间,我会向用户发出通知,并将其引导至常见问题解答,在那里他们会被轻轻地建议尝试另一个TTS引擎.

我运行一个后台计时器,在一段时间不活动后释放引擎.这段时间可由用户配置,并附带初始化延迟警告......

我知道上面的内容并没有解决你的问题,但也许我的建议会让你的用户安心,这是解决问题的第二步,但是嘿......

我毫不怀疑谷歌将逐步提高初始化性能 - 四年前,我遇到了IVONA这个问题,他最终在初始化时做得很好.