Java 精确的android线程处理程序postDelayed
我一直在尝试用Android编写节拍器,但是我发现使用Handler postdelayed方法很难准确地同步节拍。我使用ScheduledThreadPoolExecutor实现了精确的计时,但问题是,使用ScheduledThreadPoolExecutor,我无法从run方法中控制计时,因此我被迫停止并启动计划作业,这并不理想。有没有办法使处理程序更准确?还是一种不必停止和启动线程就可以重新调度ScheduledThreadPoolExecutor的方法 我目前的代码如下:Java 精确的android线程处理程序postDelayed,java,android,multithreading,threadpool,Java,Android,Multithreading,Threadpool,我一直在尝试用Android编写节拍器,但是我发现使用Handler postdelayed方法很难准确地同步节拍。我使用ScheduledThreadPoolExecutor实现了精确的计时,但问题是,使用ScheduledThreadPoolExecutor,我无法从run方法中控制计时,因此我被迫停止并启动计划作业,这并不理想。有没有办法使处理程序更准确?还是一种不必停止和启动线程就可以重新调度ScheduledThreadPoolExecutor的方法 我目前的代码如下: public
public class Metronome extends Service implements Runnable
{
private Handler handler = new Handler();
private SoundPool soundPool;
private long interval;
private void initSoundPool()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
soundPool = new SoundPool.Builder()
.setMaxStreams(1)
.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.build();
} else
{
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
}
soundId = soundPool.load(context, R.raw.secondary_clave, 1);
}
@Override
public void run()
{
handler.postDelayed(this, interval);
soundPool.play(soundId, 1, 1, 0, 0, 1);
}
public void start()
{
handler.post(this);
}
@Override
public IBinder onBind(Intent intent)
{
return null;
}
}
对于ScheduledThreadPoolExecutor,它非常精确,但是,我无法通过运行循环中的“interval”标志进行控制,因此如果我更改了interval,每次需要重新调度时,我都必须终止执行器并启动一个新的执行器,这太可怕了
public class Metronome extends Service implements Runnable
{
private SoundPool soundPool;
private long interval;
private ScheduledThreadPoolExecutor beatsPerBarExec;
private ScheduledFuture<?> futureThread;
private void initSoundPool()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
soundPool = new SoundPool.Builder()
.setMaxStreams(1)
.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.build();
} else
{
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
}
soundId = soundPool.load(context, R.raw.secondary_clave, 1);
}
@Override
public void run()
{
soundPool.play(soundId, 1, 1, 0, 0, 1);
}
public void start()
{
beatsPerBarExec = new ScheduledThreadPoolExecutor(1);
futureThread = beatsPerBarExec.scheduleAtFixedRate(this, 0, interval, TimeUnit.MILLISECONDS);
}
public void pause()
{
futureThread.cancel(false);
beatsPerBarExec.purge();
beatsPerBarExec = null;
}
@Override
public IBinder onBind(Intent intent)
{
return null;
}
}
公共类Metronome扩展服务实现可运行
{
私人声池声池;
私人长间隔;
私有ScheduledThreadPoolExecutor beatsPerBarExec;
私人计划未来;
私有void initSoundPool()
{
if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.LOLLIPOP)
{
soundPool=新建soundPool.Builder()
.setMaxStreams(1)
.setAudioAttributes(新的AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT\u TYPE\u MUSIC)
.build())
.build();
}否则
{
soundPool=新的soundPool(1,AudioManager.STREAM_MUSIC,0);
}
soundId=soundPool.load(上下文,R.raw.secondary\u clave,1);
}
@凌驾
公开募捐
{
soundPool.play(soundId,1,1,0,0,1);
}
公开作废开始()
{
beatsPerBarExec=新的ScheduledThreadPoolExecutor(1);
futuratRead=beatsPerBarExec.scheduleAtFixedRate(这个,0,间隔,时间单位,毫秒);
}
公共空间暂停()
{
futuratread.cancel(假);
beatsPerBarExec.purge();
beatsPerBarExec=null;
}
@凌驾
公共IBinder onBind(意向)
{
返回null;
}
}
您可能会看到漂移的影响
示例:您希望您的Runnable
每200秒运行一次。使用postDelayed()
在run()
方法中重新安排Runnable
,并将其作为延迟传递200秒。下次调用run()
方法时,它可能不会比上次精确地200秒。也许是210毫秒。现在,您可以重新安排Runnable
再运行200秒。这一次run()
要消除漂移,您需要确定希望运行Runnable
的确切时钟时间,减去当前时间,并在调用postDelayed()
时使用该时间。这将考虑线程计时中的任何潜在差异
请注意,当您调用postDelayed()
时,您正在发布一个Runnable
以在主(UI)线程上运行。这是处理所有UI更新的线程,当Runnable
准备好运行时,它将排队到主(UI)线程处理程序,并且可能不会立即运行
通过将Runnable
调度为在后台线程而不是主(UI)线程上运行,可以缓解此问题。但是,这意味着您的Runnable
将在后台线程上调用,我不知道您的其他代码(播放声音)是否需要在主(UI)线程上运行