1

经过一些调试,我在现有的生产代码中发现了以下内容:

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,第一个实例不再工作。)我有点震惊:除了小学生之外,我不会指望任何人会出现这样的错误。可能他们正在使用童工。或者,更有可能的是,有人无法向他的经理解释演示代码和生产代码之间的区别。

无论如何,我必须让它工作。我有一些想法,但我目前能提出的建议远非完美。任何想法如何重构它?

4

1 回答 1

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);
            }
        }
        // ...
    }
}
于 2013-02-20T05:51:34.650 回答