2012-04-14 55 views
2

我有以下类。该课程的目的是让我通过每秒显示十个字符来模拟电传打字机。android线程和处理程序最佳实践

CharacterLoopThread类的要点是查看outputBuffer,如果其中包含任何字符,请在UI线程上调用一个可运行的函数,该函数将第一个字符拖动到textView中。线程然后睡觉大约100ms。 (这里有一些诡计......虽然我在1979年使用电报时惊人,但现在对我的口味有点慢,所以每10个字符,我稍微减少延迟,当没有更多字符可显示时,我重置延迟到100毫秒...)

我编辑脱离类的底部,因为它与我的问题没有密切关系。

我在这里有什么似乎很好。但是,它是因为我还是不管我工作?你写什么线程和处理程序的首选方法?

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

(SNIP)

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; 
     } 
+2

对于初学者来说,我会* *实现Runnable接口,而不是继承* *线程。这是一种说法:“我有可能独立运行事物”,而不是“我控制线程实际启动和运行的方式”。 – Patrick 2012-04-14 01:28:54

回答

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 
}