web-dev-qa-db-ja.com

一時停止/停止および開始/再開Java TimerTask?

Java TimerTask。特定の条件に基づいて2つのTimerTaskタスクを一時停止/再開するにはどうすればよいですか?たとえば、相互に実行される2つのタイマーがあります。最初のタイマーのタスク内で満たされた場合、最初のタイマーは2番目のタイマーを停止および開始し、2番目のタイマーのタスク内で特定の条件が満たされた場合にも同じことが起こります。

public class TimerTest {
    Timer timer1;
    Timer timer2;
    volatile boolean a = false;

    public TimerTest() {
        timer1 = new Timer();
        timer2 = new Timer();     
    }

    public void runStart() {
        timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
    }

    class Task1 extends TimerTask {
        public void run() {
            System.out.println("Checking a");
            a = SomeClass.getSomeStaticValue();

            if (a) {
                // Pause/stop timer1, start/resume timer2 for 5 seconds
                timer2.schedule(new Task2(), 5000);
            }
        }
    }

    class Task2 extends TimerTask{
        public void run() {
            System.out.println("Checking a");
            a = SomeClass.getSomeStaticValue();

            if (!a) {
                // Pause/stop timer2, back to timer1
                timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
            }

            // Do something...
        }
    }

    public static void main(String args[]) {
        TimerTest tt = new TimerTest();
        tt.runStart();      
    }
}

私の質問は、timer1の実行中にtimer2を実行中にtimer2を一時停止するにはどうすればよいですか?パフォーマンスとタイミングは、実行中の別のスレッド内に実装する必要があるため、私の主な関心事です。ところで、私はこれらの並行タイマーをAndroidに実装しようとしています。

ご協力いただきありがとうございます!

19
Faiz

TimerTask.cancel() から:

繰り返しタイマータスクのrunメソッド内からこのメソッドを呼び出すと、タイマータスクが再度実行されないことが保証されます。

一度キャンセルすると、再び実行されることはありません。代わりに、より新しい ScheduledExecutorService (from Java 5+)を使用する方が良いでしょう。

Edit:基本的な構成は次のとおりです。

ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(runnable, 0, 1000, TimeUnit.MILLISECONDS);

ただし、サービスを停止せずに開始したタスクをキャンセルする方法はありません。これは少し奇妙です。

この場合、TimerTaskの方が簡単かもしれませんが、起動時に新しいインスタンスを作成する必要があります。再利用できません。

または、各タスクを個別の一時サービスとしてカプセル化することもできます。

final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
Runnable task1 = new Runnable() {
  public void run() {
    a++;
    if (a == 3) {
      exec.shutdown();
      exec = Executors.newSingleThreadScheduledExecutor();
      exec.scheduleAtFixedRate(task2, 0, 1000, TimeUnit.MILLISECONDS)
    }
  }
};
exec.scheduleAtFixedRate(task1, 0, 1000, TimeUnit.MILLISECONDS);
25
cletus

私が見つけた最も簡単な解決策:タイマータスクの実行コードにブール値を追加するだけです:

timer.schedule( new TimerTask() {
    public void run() {
       if(!paused){
           //do your thing
       }
    }
 }, 0, 1000 );
15
Song Keang

既に1つのタイマーをキャンセルしている場合、再起動することはできません。新しいタイマーを作成する必要があります。

これを参照してください answer 、ビデオとソースコードを含んでいます。

基本的に、pauseresumeの2つのメソッドがあります

一時停止中:

public void pause() {
    this.timer.cancel();
}

再開中:

public void resume() {
    this.timer = new Timer();
    this.timer.schedule( aTask, 0, 1000 );
}

それは一時停止/再開の知覚になります。

タイマーがアプリケーションの状態に基づいて異なるアクションを実行する場合、 StatePattern

拳は抽象的な状態を定義します:

abstract class TaskState  {
    public void run();
    public TaskState next();
}

そして、あなたが好きなだけの状態を提供します。重要なのは、ある状態から別の状態に導くことです。

class InitialState extends TaskState {
    public void run() {
        System.out.println( "starting...");
    }
    public TaskState next() {
         return new FinalState();
    }
 }
 class FinalState extends TaskState  {
     public void run() {
         System.out.println("Finishing...");
     }
     public TaskState next(){
         return new InitialState();
    }
 }

そして、タイマーの状態を変更します。

Timer timer = new Timer();
TaskState state = new InitialState();

timer.schedule( new TimerTask() {
     public void run() {
          this.state.run();
          if( shouldChangeState() ) {
              this.state = this.state.next();
           }
     }
 }, 0, 1000 );

最後に、同じことを異なるレートで実行する必要がある場合は、 TimingFramework の使用を検討できます。少し複雑ですが、特定のコンポーネントのペイントを異なるレートで(線形ではなく)行うことで、クールなアニメーションを作成できます。

7
OscarRyz

ソースコードを確認して、変更点を示します(以前の回答をほぼ検証します)。

Task1で:

// Stop timer1 and start timer2
timer1.cancel();
timer2 = new Timer(); // <-- just insert this line
timer2.scheduleAtFixedRate(new Task2(), 0, 1000);

そしてtask2で:

// Stop timer2 and start timer1
timer2.cancel();
timer1 = new Timer(); // <-- just insert this other
timer1.scheduleAtFixedRate(new Task1(), 0, 1000);

私のマシンで実行されます:

it works!

4
OscarRyz

私の意見では、これはやや見当違いです。コードに時間の保証が必要な場合、とにかくTimerを使用することもできません。 「このクラスはリアルタイムの保証を提供しません。Object.wait(long)メソッドを使用してタスクをスケジュールします。」

私見の答えは、タイマーを一時停止して再起動したくないということです。実行メソッドがビジネスを行わないようにしたいだけです。そしてそれは簡単です:ifステートメントでそれらをラップするだけです。スイッチがオンになり、実行され、スイッチがオフになり、そのサイクルを逃します。

編集:質問は元の質問から大幅にシフトしましたが、だれにも役立つ場合に備えてこの回答を残します。私のポイントは、イベントがNミリ秒のスパンで発生することを気にしない場合(Nミリ秒に1回は超えないということだけ)、runメソッドで条件を使用するだけです。実際、これは非常に一般的なケースで、特にNが1秒未満の場合です。

4
Dan Rosenstark

Androidは、一度スケジュールされたTimerTaskを再利用しません。そのため、タイマーとTimerTaskの両方を再インスタンス化する必要があります。たとえば、次のフラグメントです。

private Timer timer;
private TimerTask timerTask;

public void onResume ()
{
    super.onResume();

    timer = new Timer();
    timerTask = new MyTimerTask();
    timer.schedule(timerTask, 0, 1000);
}

public void onPause ()
{
    super.onPause();
    timer.cancel(); // Renders Timer unusable for further schedule() calls.
}
3

次のコードを使用して、タイマーとタスクを停止できます。

    if(null != timer)
    {

        timer.cancel();
        Log.i(LOG_TAG,"Number of cancelled tasks purged: " + timer.purge());
        timer = null;
    }

    if(task != null)
    {
        Log.i(LOG_TAG,"Tracking cancellation status: " + task.cancel());
        task = null;
    }
0
Vaibhav Agrawal
    Timer timer1;
    private boolean videoCompleteCDR=false;
    private boolean isVideoPlaying=false;   
    int videoTime=0;
    private int DEFAULT_VIDEO_PLAY_TIME = 30;  

    @Override
    public View onCreate(){
       isVideoPlaying = true; //when server response is successfully
    }


    @Override
    public void onPause() {
        super.onPause();

        if(isVideoPlaying ) {
            if(this.timer1 !=null) {
                this.timer1.cancel();
            }
        }
    }
    @Override
    public void onResume() {
        super.onResume();
        if(isVideoPlaying  && !videoCompleteCDR) {
            callTimerTask();
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (!hidden) {
            printLog( "GameFragment  visible ");
            if(isVideoPlaying  && !videoCompleteCDR) {
                callTimerTask();
            }

        } else {
            printLog("GameFragment in visible ");
            if(isVideoPlaying) {
                if(this.timer1 !=null) {
                    this.timer1.cancel();
                }
            }
        }
    }

    private void callTimerTask() {
        // TODO Timer for auto sliding
        printLog( "callTimerTask Start" );
        timer1 = new Timer();
        timer1.schedule(new TimerTask() {
            @Override
            public void run() {
                if (getActivity() != null) {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (getActivity() == null) {
                                return;
                            }
                            videoTime++;
                            if(DEFAULT_VIDEO_PLAY_TIME ==videoTime){
                                videoCompleteCDR=true;
                                Log.e("KeshavTimer", "callTimerTask videoCompleteCDR called.... " +videoTime);
                                destroyTimer();
                            }
                            Log.e("KeshavTimer", "callTimerTask videoTime " +videoTime);
                        }
                    });
                } else {
                    printLog("callTimerTask getActivity is null ");
                }
            }
        }, 1000, 1000);
        // TODO  300, 2000;
    }


    private void destroyTimer(){
        this.timer1.cancel();
    }
0
Keshav Gera