AndroidのThreadクラスをカスタマイズ

SurfaceViewを仕様していると描画用のスレッド等を使用することになります。 Threadを使用すると煩雑なことが多いです。 そこでThreadをカスタマイズしたクラスを作成します。

githubにコードを置きました。

 

1. Threadの使い方

Threadの簡単な使い方は以下の通りです。

Thread thread = new Thread(new Runnable() {
  @Override
  public void run() {
    /** Do something in Thread. */
  }
});
 
/** Start thread. */
thread.start();
 
/** Do something out of Thread. */
 
/** Stop thread. */
/** thread.stop() does not work. */
thread.interupt();
thread = null;

Runnableインターフェースのrunメソッドにスレッドで実行したい処理を記載 します。

Thread.start()を実行後、スレッドが動き出す機会が得られます。

Thread.stop()はdepricatedとなり、今や使用できません。 Thread.interupt()を実行すると、run()メソッド内でThread.sleep()を実行し た際にrunメソッド内でInterruptedExceptionが発生し、スレッドの停止を通 知することができます。

2. Runnableクラス

Runnableクラスのドキュメントによれば、RunnableインターフェースはThread クラスを拡張することなくスレッドを使用させる為のもので、Threadクラスを 拡張すべきではないと記載されています。 http://docs.oracle.com/javase/jp/6/api/java/lang/Runnable.html

しかし、Runnableクラスが複雑になりがちな点がデメリットとしてあると思い ます。例えば、描画の無限ループをsleepを交えながら実行する場合、逐一 Runnableクラスのrunメソッドで定義しなくてはなりません。 そこで、無限ループをsleepを交えながら実行できるカスタマイズされたスレッ ドクラスを定義します。

3. Threadクラスのカスタマイズ

Threadクラスを拡張したクラスと、拡張したThreadクラスのインスタンスを内 部で持つラッパークラスを作成します。 ラッパークラスのstartメソッドにて、拡張したThreadクラスを開始し、stop メソッドで拡張したThreadクラスのinterupt()実行と参照を外します。

public class WrapperThread {
 
  public class ExtendedThread extends Thread {
    public ExtendedThread() { /** Do nothing. */ }
    public void run() {
      /** Start loop. */
      while (true) {
        /** Do something. */
        try {
          /** Sleep. */
          Thread.sleep(sleepTime);
        } catch (Exception e) {
          /** Stop loop. */
          break;
        }
      }
    }
  }
 
  public void start() {
    /** Start thread. */
    mThread = new ExtendedThread();
    mThread.start();
  }
 
  public void start() {
    /** Stop thread. */
    mThread.interupt();
    mThread = null;
  }
 
}
 

加えて、sleepの秒数をFPS換算させ、Runnableを動的に変更できるようにした のが以下のコードとなります。 ラッパークラス生成後、拡張クラスの状態に関わらず、Runnableは動的に追加 と削除ができます。

/**
 * Wrapper thread class for java.lang.Thread. This class can be recycled. Once
 * Sample thread created, SampleThread.start() and SampleThread.stop() can be
 * used repeatedly.
 */
public class SampleThread {
  
  /**
   * Extended thread class for java.lang.Thread with flag for stopping thread.
   * This class cannot be recycled. SampleThread class will hide this recycle.
   */
  public class SafeStopThread extends Thread {
    
    public SafeStopThread() {
      /** Do not call super(). */
    }
    
    @Override
    public void run() {
      /** Loop start. */
      while (true) {
        long prevTime = System.currentTimeMillis() / 1000;
        
        synchronized (mRunnable) {
          for (Runnable runnable : mRunnable)
            runnable.run();
        }
        
        long postTime = System.currentTimeMillis() / 1000;
        long sleepTime = postTime - prevTime;
        
        /** Sleep at least 1 msec for avoiding ANR. */
        if (sleepTime >= mSleepTime)
          sleepTime = 1;
        
        try {
          Thread.sleep(sleepTime);
        } catch (Exception e) {
          break;
        }
      }
      
      /** Loop end. */
    }
    
  }
  
  /** Runnable done in thread loop. */
  private final List<Runnable> mRunnable;
  
  /** Extended thread class. */
  private SafeStopThread mThread;
  
  /** Sleep time each thread loop. */
  private final long mSleepTime;
  
  /**
   * @param FPS
   *          Frame per second for thread loop.
   */
  public SampleThread(int FPS) {
    mRunnable = new ArrayList<Runnable>();
    mSleepTime = FPS / 1000;
  }
  
  public void addRunnable(Runnable runnable) {
    synchronized (mRunnable) {
      mRunnable.add(runnable);
    }
  }
  
  public void removeRunnable(Runnable runnable) {
    synchronized (mRunnable) {
      mRunnable.remove(runnable);
    }
  }
  
  public void start() {
    mThread = new SafeStopThread();
    mThread.start();
  }
  
  public void stop() {
    /** Throw InterruptedException. */
    mThread.interrupt();
    mThread = null;
  }
  
}