Android: принудительное выполнение основного цикла выполнения до завершения текущего потока выполнения

На iOS, если я хочу, чтобы мой текущий поток выполнения ждал (т.е. блок), и основной цикл выполнялся так, чтобы мог выполняться поток выполнения, следующий в основной очереди, я вызываю:

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

Как бы я сделал эквивалент на Android?

3 ответа

Решение

Извините, что разочаровал вас, но в Android невозможно сделать то, что вы просите.

Это действительно возможно сделать в Android. Ответ Шахара на правильном пути. Проблема не в том, что основной цикл будет блокироваться (если код не был выполнен в основном потоке, но вопрос не в этом). Проблема в том, что другой поток не блокирует, а просто зацикливает и записывает циклы ЦП в цикле while. Вот блокирующий прогон основного метода, который я использую в своем приложении:

/**
 * 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 это простой класс, в моем случае частный внутренний класс для класса, который реализует blockingRunOnMain():

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

blockingRunOnMain() используется путем передачи его Runnable запустить в основном потоке:

blockingRunOnMain(new Runnable() {

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

Первая часть blockingRunOnMain() Метод проверяет, вызывается ли метод из основного потока и, если это так, просто выполняет код немедленно. Поскольку функция blockingRunOnMain() это синхронно запустить Runnable код до возврата метода, это будет иметь тот же результат, даже если вызывается из самого основного потока.

Если метод вызывается из потока, отличного от основного потока, мы затем публикуем Runnable к Handler который связан с основным потоком Looper, После публикации Runnable Параметр, мы тогда публикуем другой Runnable который будет выполняться после Runnable параметр завершает выполнение, так как Handler выполняет размещены Messageс и Runnableв порядке. Этот второй Runnable служит для уведомления заблокированного потока о том, что работа в основном потоке завершена.

После публикации второго Runnable Теперь мы блокируем фоновый поток и ждем, пока нас не уведомят. Важно синхронизировать операции, выполняемые на lock так что операции являются атомарными в каждом потоке.

Фоновый поток вызывает wait() на мониторе и ждет пока mRunComplete == true, Если он получает InterruptedExceptionВажно продолжать ожидание и восстанавливать прерванное состояние потока после того, как мы закончим, поскольку мы сами не используем механизм прерываний для отмены нашей задачи, его восстановление позволяет другому методу, расположенному выше в стеке вызовов, обрабатывать прерывание. Смотрите "Работа с InterruptedException".

Когда Runnable параметр завершил выполнение и второй опубликовал Runnable выполняет, он просто устанавливает mRunComplete true и уведомляет заблокированный поток о продолжении выполнения, обнаружив mRunComplete == true сейчас возвращается из blockingRunOnMain()выполнив Runnable параметр синхронно в главном потоке пользовательского интерфейса.

Один короткий обходной путь должен иметь логическое значение, которое изменяется следующим циклом основного потока.

запуск в главном потоке может быть сделан с runOnUIthread (или получить основной петлитель самостоятельно) переход к следующему циклу может быть легко сделан с handler.postDelayed(Runnable run, long delayMills)и без задержки времени.

так что вы можете сделать это:

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) {
    ;
}
Другие вопросы по тегам