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) {
;
}