2

我有以下课程。该课程的目的是让我通过每秒显示大约十个字符来模拟电传打字机/打字机。

CharacterLoopThread 类的重点是查看 outputBuffer,如果其中有任何字符,则在 UI 线程上调用一个可运行的对象,该线程会提取第一个字符并将其放入 textView。然后线程休眠大约 100 毫秒。(这里有一些恶作剧……虽然电传打字机在我 1979 年使用的时候很棒,但现在我的口味有点慢。所以每 10 个字符,我稍微减少延迟。当没有更多字符可以显示时,我将延迟重置为 100 毫秒...)

我编辑了课程的底部,因为它与我的问题无关。

我在这里所拥有的似乎运作良好。然而,它是因为我还是不管我?您首选的编写线程和处理程序的方式是什么?

public class MyActivity extends Activity {
    private TextView textView;
    private ScrollView scrollView;
    private StringBuilder outputBuffer;
    private Handler handler;
    private CharacterLooperThread characterLooperThread;

(剪断)

   private class CharacterLooperThread extends Thread {
        private boolean allowRun;
        private Runnable run;
        int effectiveCharacterDelay;
        int characterCount;

        public CharacterLooperThread() {
            allowRun = true;

            run = new Runnable() {
                public void run() {
                    /**
                     * Don't do anything if the string has been consumed. This is necessary since when the delay
                     * is very small it is possible for a runnable to be queued before the previous runnable has
                     * consumed the final character from the outputBuffer. The 2nd runnable will cause an
                     * exception on the substring() below.
                     */
                    if (outputBuffer.length() == 0) return;

                    try {
                        textView.append(outputBuffer.substring(0, 1));
                        scrollToBottom();
                        outputBuffer.deleteCharAt(0);
                    } catch (Exception e) {
                        toast(getMsg(e));
                    }
                }
            };
        }

        public void run() {
            resetDelay();
            while (allowRun) {

                /**
                 * This if() performs 2 functions:
                 * 1. It prevents us from queuing useless runnables in the handler.  Why use the resources if
                 * there's nothing to display?
                 * 2. It allows us to reset the delay values.  If the outputBuffer is depleted we can reset the
                 * delay to the starting value.
                 */
                if (outputBuffer.length() > 0) {
                    handler.post(run);
                    reduceDelay();
                } else {
                    resetDelay();
                }
                try {
                    Thread.sleep(effectiveCharacterDelay);
                } catch (InterruptedException e) {
                    toast("sleep() failed with " + e.getMessage());
                }
            }
            /**
             * Make sure there's no runnable on the queue when the thread exits.
             */
            handler.removeCallbacks(run);
        }

        public void exit() {
            allowRun = false;
        }
4

1 回答 1

1

一种想法是用来Handler.postDelayed安排单独的“击键”。您可以一次完成所有操作,也可以在每次击键Runnable完成后安排下一次击键;如果处理落后于计划,前一种方法将尽快赶上,而后者基本上会将所有内容推迟以保持相同的击键间延迟。

看到一个线程改变了StringBuilder另一个线程读取它,我很担心。(StringBuilder该类是 . 的非线程安全继承者StringBuffer,它是在人们认为使单个类线程安全是好的设计时写回的)。如果它偶尔不会做一些意想不到的事情,那么你很幸运,尽管这里不会出错。但是,使用postDelayed,您可能会完全摆脱后台线程。

只要您正在创建匿名Runnable类,请注意您可以为它们提供参数(只要您声明变量final)。因此,我倾向于一次向每个 发布一个字符Runnable,如下所示:

long delay = 0;
for (int j = 0; j < outputBuffer.length(); ++j) {
    final CharSequence s = outputBuffer.subSequence(j, j + 1);
    handler.postDelayed(new Runnable() {
        @Override public void run() {
            textView.append(s);
            scrollToBottom();
        }
    }, delay);
    delay += 100; // or whatever
}
于 2012-04-20T00:42:22.640 回答