runOnUiThread() does not seem to work when being executed within a thread. Anybody knows of a workaround?
Note: I filed a ticket here - https://github.com/robolectric/robolectric/issues/2479
import android.app.Activity;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertTrue;
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class RunOnUiThreadTest {
/**
* Works
* @throws Exception
*/
@Test
public void inside_the_main_thread() throws Exception {
final Activity activity = Robolectric.setupActivity(Activity.class);
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean didRun = new AtomicBoolean(false);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
didRun.set(true);
latch.countDown();
}
});
latch.await(20, TimeUnit.SECONDS);
assertTrue(didRun.get());
}
/**
* Fails
* @throws Exception
*/
@Test
public void inside_a_new_thread() throws Exception {
final Activity activity = Robolectric.setupActivity(Activity.class);
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean didRun = new AtomicBoolean(false);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
didRun.set(true);
latch.countDown();
}
});
}
});
thread.start();
latch.await(20, TimeUnit.SECONDS);
assertTrue(didRun.get());
}
}
The results to the tests are likely as expected since the second test blocks the UI thread which is required to run the target Runnable
.
The first test will pass because the whole method is executed synchronously. Since you are already on the UI thread, when calling activity.runOnUiThread(...)
the Runnable
will be executed immediately. Since the latch
is then 0 latch.await(20, TimeUnit.SECONDS)
returns immediately and the assert is true.
In the second example you start a new thread and call activity.runOnUiThread(...)
from within it. Since you are not on the UI thread the Runnable
is posted to a Handler
on the UI thread to be executed in the future asynchronously. At this point you proceed to call latch.await(20, TimeUnit.SECONDS);
from the UI thread which means the Runnable
cannot run during this time since you are blocking the UI thread.
After 20 seconds the thread resumes but the Runnable
has never had the chance to run so the assert fails since didRun()
was never set to true.
Below is an updated test that allows the Runnable
posted using runOnUiThread()
to run.
@Test
public void inside_a_new_thread() throws Exception {
final Activity activity = Robolectric.setupActivity(Activity.class);
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean didRun = new AtomicBoolean(false);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
didRun.set(true);
latch.countDown();
}
});
}
});
thread.start();
// sleep current thread to give the new thread a chance to run and post the runnable
Thread.sleep(1000);
// This method will cause the runnable to be executed
Robolectric.flushForegroundThreadScheduler();
// Should immediately return since the runnable has been executed
latch.await(20, TimeUnit.SECONDS);
assertTrue(didRun.get());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With