Question

On iOS, if I want my current thread of execution to wait (ie. block) and the main loop to run so that the thread of execution next in the main queue can execute, I invoke:

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];

How would I go about doing the equivalent on Android?

Was it helpful?

Solution

I'm sorry to disappoint you, but it is not possible to do what you're asking for in Android.

OTHER TIPS

This is indeed possible to do in Android. Shachar's answer is on the right track. The problem is not that the main loop will block (unless the code was executed on the main thread, but that's not what the question is proposing). The problem is that the other thread doesn't block, but is simply looping and burning CPU cycles in the while loop. Here is a blocking run on main method I use in my app:

/**
 * Runs the runnable on the main UI thread. If called from a thread other than the UI thread,
 * this method will block the calling thread and return only after the runnable has completed
 * execution on the main UI thread.
 * @param runnable Runnable to run on the main UI thread
 */
public static void blockingRunOnMain(Runnable runnable) {

    if (Looper.myLooper() == Looper.getMainLooper()) { // Already on UI thread run immediately
        runnable.run();
    }
    else { // Queue to run on UI thread
        final MainRunMonitor lock = new MainRunMonitor();
        Handler mainHandler = new Handler(Looper.getMainLooper());
        mainHandler.post(runnable);
        // Task to notify calling thread when runnable complete
        mainHandler.post(new Runnable() {

            @Override
            public void run() {
                synchronized (lock) {
                    lock.mRunComplete = true;
                    lock.notify();
                }
            }
        });
        // Block calling thread until runnable completed on UI thread
        boolean interrupted = false;
        try {
            synchronized (lock) {
                while (!lock.mRunComplete) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        // Received interrupt signal, but still haven't been notified, continue waiting
                        interrupted = true;
                    }
                }
            }
        } finally {
            if (interrupted) {
                Thread.currentThread().interrupt(); // Restore interrupt to be used higher on call stack (we're not using it to interrupt this task)
            }
        }
    }
}

MainRunMonitor is a simple class, in my case a private inner class to the class that implements blockingRunOnMain():

/**
 * Monitor to lock calling thread while code is executed on UI thread.
 */
private static class MainRunMonitor {
    private boolean mRunComplete = false;
}

blockingRunOnMain() is used by passing it a Runnable to run on the main thread:

blockingRunOnMain(new Runnable() {

    @Override
    public void run() {
        workToDoSynchronouslyOnMain();
    }
});

The first part of the blockingRunOnMain() method checks if the method is being called from the main thread and if so, simply executes the code immediately. Since the function of blockingRunOnMain() is to synchronously run the Runnable code before the method returns, this will have this same result even if called from the main thread itself.

If the method is called from a thread other than the main thread, we then post the Runnable to a Handler which is bound to the main thread's Looper. After posting the Runnable parameter, we then post another Runnable that will execute after the Runnable parameter completes execution, since the Handler executes posted Messages and Runnables in order. This second Runnable serves to notify the blocked thread that the work has been completed on the main thread.

After posting the second Runnable we now block the background thread and wait until we're notified. It's important to synchronize the operations performed on lock so that the operations are atomic on each thread.

The background thread calls wait() on the monitor and waits until mRunComplete == true. If it gets an InterruptedException, it's important to continue waiting and restore the interrupted state of the thread after we're done, since we're not using the interrupt mechanism ourselves to cancel our task, restoring it allows another method higher on the call stack to handle the interrupt. See "Dealing with InterruptedException".

When the Runnable parameter has completed execution and the second posted Runnable executes, it simply sets mRunComplete to true and notifies the blocked thread to continue execution, which finding mRunComplete == true now returns from blockingRunOnMain(), having executed the Runnable parameter synchronously on the main UI thread.

One short workaround is to have a boolean that is changed by the next main thread loop.

running on main thread can be done with runOnUIthread (or getting the main looper yourself) moving to the next loop can b easely done with handler.postDelayed(Runnable run, long delayMills), and a no-time delay.

so you could do this:

nextMainLoopDone = false;//This should be changed to a thread safe boolean, could use AtomicBoolean
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        nextMainLoopDone = true;
    }
}, 1/* delay for no time, just to next loop*/);

while(!nextMainLoopDone) {
    ;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top