I have an Android service which must offload a task to a different thread but schedule the response callback unto the calling thread. After much thinking I came up with this code:
public void testRequestingAScheduleUpdateShouldNotBlockAndDeliverItOnTheCallingThread() throws Exception {
final StreamSchedule mockSchedule = new StreamSchedule(
new StreamMetadata("TyPhone", "Tidal Waves 028"),
new StreamMetadata[] { new StreamMetadata(null, "The Progmatic Radio Show 002 with Ashley Bonsall") },
new StreamMetadata[] { new StreamMetadata("TEKNO", "Sound Escalation 065 with Xabi") }
);
final boolean[] threadBlocked = {true};
LooperThread looperThread = new LooperThread();
looperThread.start();
StreamScheduleProvider mockScheduleProvider = mock(StreamScheduleProvider.class);
when(mockScheduleProvider.getCurrentStreamSchedule()).then(invocation -> {
threadBlocked[0] = Thread.currentThread().getId() == looperThread.getId();
return mockSchedule;
});
DataAccessModule.scheduleProviderOverride = mockScheduleProvider;
final long[] callingThreadId = {-1};
ScheduleUpdateListener updateListener = mock(ScheduleUpdateListener.class);
doAnswer(invocation -> {
callingThreadId[0] = Thread.currentThread().getId();
looperThread.stopLooper();
return null;
}).when(updateListener).onScheduleChanged(any(StreamSchedule.class));
PlaybackServiceBinder serviceBinder = (PlaybackServiceBinder) bindService(getServiceIntent());
serviceBinder.subscribeScheduleUpdates(updateListener);
looperThread.getHandler().post(serviceBinder::updateSchedule);
looperThread.join();
verify(updateListener).onScheduleChanged(mockSchedule);
assertFalse("The thread was blocked, this is unacceptable", threadBlocked[0]);
assertEquals("The update callback was not called on the requesting thread", callingThreadId[0], looperThread.getId());
}
class LooperThread extends Thread {
private Handler handler;
@Override
public void run() {
Looper.prepare();
handler = new Handler(Looper.myLooper());
Looper.loop();
}
public Handler getHandler() {
return handler;
}
public void stopLooper() {
handler.getLooper().quit();
}
}
It does work (the test fails if I simply execute the callback on the background thread or if I use no background thread at all) but I have this that intense itch I overlooked something that could make this more readable/elegant. Any ideas?