但它似乎无法解决我的问题。
我有一个自定义计时器,当我点击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);
答案 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)您正在使用_interval
和PausableTimer.this._interval
:您的意思是这样做吗?