Reputation: 31
I am building a metronome in android with sound and a visual blink and of course both things need to be in sync. The problem is that the audio processing happens inside a background thread and as we all very well know "only the thread that created the thread hierarchy can change it's views" therefore I needed a way to get the UI thread to change the veiew. So how can I update the ui thread in sync with the beat I am producing? Both things don't seem synchronized. Is there a better way to achieve this than the method I depict below?
This is the current code I have:
public class MetronomeService extends Service implements Runnable
{
//Thread
private HandlerThread handlerThread;
private Handler backgroundHandler;
private Handler uiHandler
//Interface to the Fragment that owns the views
@Setter
private Listener listener;
//Colour view
private ColourViewManager colour = null;
@Override
public void onCreate()
{
handlerThread = new HandlerThread("PlayerHandlerThread", Process.THREAD_PRIORITY_AUDIO);
handlerThread.start();
//For packages of work in the background
this.backgroundHandler = new Handler(handlerThread.getLooper());
//To send messages to the UI
this.uiHandler = new Handler(Looper.getMainLooper())
{
@Override
public void handleMessage(@NonNull Message msg)
{
switch (msg.what)
{
case 0:
{
listener.resetState();
break;
}
case 1:
{
colour = listener.blink();
break;
}
}
}
};
super.onCreate();
}
@Override
public void run()
{
uiHandler.sendEmptyMessage(0);
audioGenerator.playTrack(bpm);
while (playingState.get() == TimerState.PLAYING)
{
uiHandler.sendEmptyMessage(1);
if (colour == PulsingColourToggleButton.PulsingColour.YELLOW)
{
audioGenerator.writeSound(AudioGenerator.TICKING);
} else if (colour == PulsingColourToggleButton.PulsingColour.RED)
{
audioGenerator.writeSound(AudioGenerator.TOCKING);
}
audioGenerator.writeSound(AudioGenerator.SILENCE);
}
audioGenerator.destroy();
colour = null;
}
public void start()
{
backgroundHandler.post(this);
}
}
Then on the Fragment class I call the service with a simple
service.start()
Upvotes: 0
Views: 226
Reputation: 476
I don't know what you are doing exactly but to change the view from service you need to use LocalBroadcast so check this
In your service create the LocalBradcastManager
val broadcaster = LocalBroadcastManager.getInstance(baseContext)
val intent = Intent("update")
intent.putExtra("orderId", orderId)
intent.putExtra("status", status)
intent.putExtra("data", date)
broadcaster.sendBroadcast(intent)
And in your view
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
try {
val orderId = intent.getStringExtra("orderId")
val status = intent.getStringExtra("status")
val date = intent.getStringExtra("date")
if (model.details.order_information[0].id.toString() == orderId) {
if ("Request Order".contains(status!!))
binding.requestTime.text = date
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
LocalBroadcastManager.getInstance(App.instance).registerReceiver(receiver, IntentFilter("update"))
This is an example of the logic you have to do
Upvotes: 0