ScheduledExecutorService 仅触发一次
ScheduledExecutorService Is Only Firing Once
我看了
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/Timer: Reset timer
D/Timer: Starting execution
D/Timer: Fire
D/Timer: Reset timer
D/Timer: Starting execution
D/Timer: Fire
D/Timer: Fire
D/Timer: Reset timer
D/Timer: Starting execution
为了更加清楚,这是我使用的一些调用代码:
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();
}
解决方案
在发现我的 run()
中的 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) 您设置了 _isRunning = true;
并且从未将其重置为 false
;
2) 你在哪里设置_duration
?如果这是 0
,您的计时器将永远不会结束。
3) 您正在使用 _interval
和 PausableTimer.this._interval
:您是要这样做吗?
问题的起因非常有趣(至少对我而言)。
这个问题是我触发的 onTick()
回调:
我注意到我的日志有些奇怪。 onTick()
之前的 run()
日志被触发,而它下面的那些没有:
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);
我看了 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/Timer: Reset timer
D/Timer: Starting execution
D/Timer: Fire
D/Timer: Reset timer
D/Timer: Starting execution
D/Timer: Fire
D/Timer: Fire
D/Timer: Reset timer
D/Timer: Starting execution
为了更加清楚,这是我使用的一些调用代码:
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();
}
解决方案
在发现我的 run()
中的 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) 您设置了 _isRunning = true;
并且从未将其重置为 false
;
2) 你在哪里设置_duration
?如果这是 0
,您的计时器将永远不会结束。
3) 您正在使用 _interval
和 PausableTimer.this._interval
:您是要这样做吗?
问题的起因非常有趣(至少对我而言)。
这个问题是我触发的 onTick()
回调:
我注意到我的日志有些奇怪。 onTick()
之前的 run()
日志被触发,而它下面的那些没有:
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);