ScheduledExecutorService仅触发一次

时间:2016-07-08 00:53:15

标签: java timer scheduled-tasks

我看着 ScheduledExecutorService only loops once

但它似乎无法解决我的问题。

我有一个自定义计时器,当我点击start时,它应该每秒触发一次回调:

/**
 * Starts the timer. If the timer was already running, this call is ignored.
 */
public void start()
{
    if (_isRunning)
    {
        return;
    }

    _isRunning = true;

    // Schedules repeated task that fires each time at the interval given
    Log.d("Timer", "Starting execution");
    _future = _execService.scheduleWithFixedDelay(new Runnable()
    {
        @Override
        public void run()
        {
            Log.d("Timer", "Fire");

            _elapsedTime += PausableTimer.this._interval;

            // If time has exceeded duration, stop timer
            if (_duration > 0 && _elapsedTime >= _duration)
            {
                Log.d("Timer", "Finish");
                onFinish();
                _future.cancel(false);
            }
        }
    }, 0, _interval, TimeUnit.MILLISECONDS);
}

以下是我调用计时器的方法:

    _timer = new PausableTimer(1000, PausableTimer.DURATION_INFINITY);
    _timer.start();

这是创建一个计时器,以1000毫秒的间隔触发以获得灵活性。

但是我的日志只显示一次触发。 "完成"日志没有出现所以我知道它还没有结束。

你知道为什么这只是一次射击吗?

更新

/**
 * Timer that can play and pause.
 */
public class PausableTimer extends Timer
{
    public static final int DURATION_INFINITY = -1;

    private Callbacks.VoidCallback _onTick;
    private Callbacks.VoidCallback _onFinish;

    private volatile boolean _isRunning = false;
    private long _interval;
    private long _elapsedTime;
    private long _duration;
    private ScheduledExecutorService _execService = Executors.newSingleThreadScheduledExecutor();
    private Future<?> _future = null;

    /**
     * Creates a pausable timer.
     * @param interval The time gap between each tick in millis.
     * @param duration The period in millis for which the timer should run.
     * Set it to {@code Timer#DURATION_INFINITY} if the timer has to run indefinitely.
     */
    public PausableTimer(long interval, long duration)
    {
        _interval    = interval;
        _duration    = duration;
        _elapsedTime = 0;
        _isRunning   = false;
    }




    /// LIFE CYCLE



    /**
     * Starts the timer. If the timer was already running, this call is ignored.
     */
    public void start()
    {
        if (_isRunning)
        {
            Log.d("Timer", "already started running");
            return;
        }

        _isRunning = true;

        // Schedules repeated task that fires each time at the interval given
        Log.d("Timer", "Starting execution");
        _future = _execService.scheduleWithFixedDelay(new Runnable()
        {
            @Override
            public void run()
            {
                Log.d("Timer", "Fire");
                onTick();

                _elapsedTime += PausableTimer.this._interval;

                // If time has exceeded duration, stop timer
                if (_duration > 0 && _elapsedTime >= _duration)
                {
                    Log.d("Timer", "Finish");
                    onFinish();
                    _future.cancel(false);
                }
            }
        }, 0, PausableTimer.this._interval, TimeUnit.MILLISECONDS);
    }

    /**
     * Pauses the timer.
     */
    public void pause()
    {
        if(!_isRunning)
        {
            return;
        }

        _future.cancel(false);
        _isRunning = false;
    }

    /**
     * Resumes the timer if it was paused, else starts the timer.
     */
    public void resume()
    {
        start();
    }

    /**
     * Called periodically with the _interval set as the delay between subsequent calls.
     * Fires tick callback if set.
     */
    private void onTick()
    {
        if (_onTick != null)
        {
            _onTick.callback();
        }
    }

    /**
     * Called once the timer has run for the specified _duration.
     * If the _duration was set as infinity, then this method is never called.
     * Fires finished callback if set.
     */
    protected void onFinish()
    {
        if (_onFinish != null)
        {
            _onFinish.callback();
        }

        _isRunning = false;
    }

    /**
     * Stops the timer. If the timer is not running, then this call does nothing.
     */
    public void cancel()
    {
        pause();
        _elapsedTime = 0;
    }




    /// GETTERS



    /**
     * @return the elapsed time (in millis) since the start of the timer.
     */
    public long getElapsedTime()
    {
        return _elapsedTime;
    }

    /**
     * @return the time remaining (in millis) for the timer to stop.
     * If the _duration was set to {@code Timer#DURATION_INFINITY}, then -1 is returned.
     */
    public long getRemainingTime()
    {
        if (_duration <= PausableTimer.DURATION_INFINITY)
        {
            return PausableTimer.DURATION_INFINITY;
        }

        return _duration - _elapsedTime;
    }






    /// BINDERS



    /**
     * @return true if the timer is currently running, and false otherwise.
     */
    public boolean isRunning()
    {
        return _isRunning;
    }

    /**
     * Binds onTick callback.
     */
    public void bindOnTick(Callbacks.VoidCallback callback)
    {
        _onTick = callback;
    }

    /**
     * Binds onFinish callback.
     * @param callback
     */
    public void bindOnFinish(Callbacks.VoidCallback callback)
    {
        _onFinish = callback;
    }
}

这是一个典型的日志。基本上发生的事情是我启动它,等待10-15秒,然后再次启动它。它应该每秒发射一次。然而它会发射一两次,然后在重新启动之前不会发射。

  

D /定时器:重置定时器

     

D / Timer:开始执行

     

D / Timer:Fire

     

D /定时器:重置定时器

     

D / Timer:开始执行

     

D / Timer:Fire

     

D / Timer:Fire

     

D /定时器:重置定时器

     

D / Timer:开始执行

要更清楚,这里是我使用的一些调用代码:

private void restartTimer()
{
    if (_timer != null)
    {
        _timer.cancel();
    }
    Log.d("Timer", "Reset timer");

    _timer = new PausableTimer(1000, PausableTimer.DURATION_INFINITY);
    _timer.bindOnTick(new Callbacks.VoidCallback()
    {
        @Override
        public void callback()
        {
            decrementTimeRemaining();
        }
    });
    _timer.start();
}

在确定onTick()中的run()调用导致线程停止后,我通过将onTick()调用发送到主线程来解决这个问题:

     _future = _execService.scheduleWithFixedDelay(new Runnable()
    {
        @Override
        public void run()
        {
            Log.d("Timer", "Run begin");

            Runnable task = new Runnable()
            {
                @Override
                public void run()
                {
                    Log.d("Timer", "Main thread tick");
                    PausableTimer.this.onTick();
                }
            };
            Handler mainHandler = new Handler(Looper.getMainLooper());
            mainHandler.post(task);

            _elapsedTime += PausableTimer.this._interval;
            Log.d("Timer", "Run middle");


            // If time has exceeded duration, stop timer
            if (_duration > 0 && _elapsedTime >= _duration)
            {
                Log.d("Timer", "Finish");
                onFinish();
                _future.cancel(false);
            }
        }
    }, 0, PausableTimer.this._interval, TimeUnit.MILLISECONDS);

2 个答案:

答案 0 :(得分:1)

问题非常有趣(至少对我而言)。

这个问题是我正在解雇的onTick()回调:

我注意到我的日志非常奇怪。 run()之前的onTick()日志正在解雇,而其下方的日志则没有:

    Log.d("Timer", "Starting execution");
    _future = _execService.scheduleWithFixedDelay(new Runnable()
    {
        @Override
        public void run()
        {
            Log.d("Timer", "Run begin"); // fires 

            onTick(); // when I remove this, all below logs fire!

            _elapsedTime += PausableTimer.this._interval;
            Log.d("Timer", "Run middle"); // didn't fire
            Log.d("Timer", "Elapsed time " + _elapsedTime); // didn't fire
            Log.d("Timer", "Duration " + _duration); // didn't fire

            // If time has exceeded duration, stop timer
            if (_duration > 0 && _elapsedTime >= _duration)
            {
                Log.d("Timer", "Finish"); // didn't fire
                onFinish();
                _future.cancel(false);
            }

            Log.d("Timer", "Run End"); // didn't fire
        }
    }, 0, PausableTimer.this._interval, TimeUnit.MILLISECONDS);

当我删除onTick()所有已触发的日志时。

当我尝试使用onTick()从此处继续主线程时,我怀疑某些内容被搞砸了。

我还不确定,但这就是计时器只发射一次,onTick()通话打乱它的原因。

我会继续进一步调查,并愿意接受您的意见。

<强>解决方案 在主线程上调度回调:

    _future = _execService.scheduleWithFixedDelay(new Runnable()
    {
        @Override
        public void run()
        {
            Log.d("Timer", "Run begin");

            Runnable task = new Runnable()
            {
                @Override
                public void run()
                {
                    Log.d("Timer", "Main thread tick");
                    PausableTimer.this.onTick();
                }
            };
            Handler mainHandler = new Handler(Looper.getMainLooper());
            mainHandler.post(task);

            _elapsedTime += PausableTimer.this._interval;
            Log.d("Timer", "Run middle");


            // If time has exceeded duration, stop timer
            if (_duration > 0 && _elapsedTime >= _duration)
            {
                Log.d("Timer", "Finish");
                onFinish();
                _future.cancel(false);
            }
        }
    }, 0, PausableTimer.this._interval, TimeUnit.MILLISECONDS);

答案 1 :(得分:0)

1)您设置了_isRunning = true;,但从未将其重置为false;

2)你在哪里设置_duration?如果这是0,您的计时器将永远不会完成。

3)您正在使用_intervalPausableTimer.this._interval:您的意思是这样做吗?