经过一些调试后,我在现有的生产代码中找到了以下内容:
public class SomeTTS {
private static TTSSpeechListener mSpeechListener;
private static TTSBreakListener mBreakListener;
// more static (!!!) listeners
private String mPath;
public TTS(String path, TTSSpeechListener speechListener)
throws RuntimeException {
// ...
mSpeechListener = speechListener;
mBreakListener = null;
// more listeners set to null
// ...
}
// called from NATIVE code that I cannot change
private static void onReceiveSpeechData(byte[] samples) {
mSpeechListener.onSpeechData(samples);
}
// ...
}
该类是本机库(Android,NDK,JNI)的包装器。我无权访问本机库源。 (当然,你看到了问题:在我创建SomeTTS
的第二个实例后,第一个实例不再起作用了。)我有点震惊:除了一个小学生,我不会想到这样的错误。可能他们正在使用童工。或者,更有可能的是,有人无法向他的经理解释演示和生产代码之间的区别。
无论如何,我必须让它发挥作用。我有一些想法,但我现在提出的建议远非完美。任何想法如何重构它?
答案 0 :(得分:0)
这就是我所做的:我让课程破碎并将其包装在一个提供合理API的课程中。
由于它是TTS,并且让TTS一次只说出一个请求是合理的,我使用了一个指向当前SomeTTS实例的静态指针,并使调用同步。我无法修复SomeTTS
类,但我可以确保我对其方法的调用是正确的 - 这意味着我不会调用某些方法。
public class SomeTTS {
private TTSSpeechListener mSpeechListener;
// ... more listeners, NOT static ...
private static volatile SomeTTS currentTTS;
protected static Object currentTtsAccessLock = new Object();
public static TTSSpeechListener currentSpeechListener() {
synchronized (currentTtsAccessLock) {
return currentTTS == null ? null : currentTTS.mSpeechListener;
}
}
public static void setCurrentTTS(SomeTTS tts) {
synchronized (currentTtsAccessLock) {
currentTTS = tts;
}
}
// called from NATIVE code that I cannot change
private static void onReceiveSpeechData(byte[] samples) {
TTSSpeechListener speechListener = currentSpeechListener();
if (speechListener != null) {
speechListener.onSpeechData(samples);
} else {
// Just no better idea. But this should never happen.
Log.e("SomeTTS","ERROR: speechListener == null samples: "+samples.length);
}
}
// ...
}
这就是全班同学。仍然可以对其方法进行不正确的调用;该学科将使用MyTTSEngine
代替。该类MyTTSEngine
提供了合理的API; SomeTTS
没有。
class MyTTSEngine {
private SomeTTS mTTS;
private Object upperLevelTtsLock = new Object();
// ...
public void say(String text) {
// ...
synchronized (upperLevelTtsLock) {
TTS.setCurrentTTS(mTTS);
try {
mTTS.clearAbortState();
mTTS.synthesizeFromString(text);
} finally {
TTS.setCurrentTTS(null);
}
}
// ...
}
}