У меня есть рабочий поток, который создает runnable-объект и вызывает runOnUiThread на нем, потому что он имеет дело с представлениями и элементами управления. Я хотел бы сразу использовать результат работы объекта runnable. Как дождаться окончания? Меня это не беспокоит, если он блокирует.
Как дождаться завершения runOnUiThread для Android?
Ответ 1
Просто поцарапайте основные моменты
synchronized( myRunnable ) {
activity.runOnUiThread(myRunnable) ;
myRunnable.wait() ; // unlocks myRunable while waiting
}
Между тем... в myRunnable...
void run()
{
// do stuff
synchronized(this)
{
this.notify();
}
}
Ответ 2
Возможно, немного упрощенное, но мьютекс выполнит эту работу:
final Semaphore mutex = new Semaphore(0);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
// YOUR CODE HERE
mutex.release();
}
});
try {
mutex.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
Ответ 3
Эндрю ответ хороший, я создаю класс для более легкого использования.
Выполнение интерфейса:
/**
* Events for blocking runnable executing on UI thread
*
* @author
*
*/
public interface BlockingOnUIRunnableListener
{
/**
* Code to execute on UI thread
*/
public void onRunOnUIThread();
}
Реализация класса:
/**
* Blocking Runnable executing on UI thread
*
* @author
*
*/
public class BlockingOnUIRunnable
{
// Activity
private Activity activity;
// Event Listener
private BlockingOnUIRunnableListener listener;
// UI runnable
private Runnable uiRunnable;
/**
* Class initialization
* @param activity Activity
* @param listener Event listener
*/
public BlockingOnUIRunnable( Activity activity, BlockingOnUIRunnableListener listener )
{
this.activity = activity;
this.listener = listener;
uiRunnable = new Runnable()
{
public void run()
{
// Execute custom code
if ( BlockingOnUIRunnable.this.listener != null ) BlockingOnUIRunnable.this.listener.onRunOnUIThread();
synchronized ( this )
{
this.notify();
}
}
};
}
/**
* Start runnable on UI thread and wait until finished
*/
public void startOnUiAndWait()
{
synchronized ( uiRunnable )
{
// Execute code on UI thread
activity.runOnUiThread( uiRunnable );
// Wait until runnable finished
try
{
uiRunnable.wait();
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
}
}
}
Использование:
// Execute an action from non-gui thread
BlockingOnUIRunnable actionRunnable = new BlockingOnUIRunnable( yourActivity, new BlockingOnUIRunnableListener()
{
public void onRunOnUIThread()
{
// Execute your activity code here
}
} );
actionRunnable.startOnUiAndWait();
Ответ 4
Решение может заключаться в использовании Java FutureTask<T>
, который имеет то преимущество, что кто-то еще затронул все потенциальные проблемы concurrency для вас:
public void sample(Activity activity) throws ExecutionException, InterruptedException {
Callable<Void> callable = new Callable<Void>() {
@Override
public Void call() throws Exception {
// Your task here
return null;
}
};
FutureTask<Void> task = new FutureTask<>(callable);
activity.runOnUiThread(task);
task.get(); // Blocks
}
Вы даже можете вернуть результат из основного потока, заменив Void
на что-то еще.
Ответ 5
Я думаю, что самый простой способ добиться этого - использовать "CountDownLatch".
final CountDownLatch latch = new CountDownLatch(1);
runOnUiThread(new Runnable() {
@Override
public void run() {
// Do something on the UI thread
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Now do something on the original thread
(Я считаю, что этот вопрос является дубликатом Как сделать задачу таймера ждать до завершения runOnUiThread)
Ответ 6
На случай, если кто-то столкнется с этим во время разработки в приложении Xamarin, я оставлю здесь свой С# -код, который сделал свое дело.
Мой адаптер RecyclerView был установлен слишком поздно, поэтому он возвращал значение null в моем конструкторе ScrollListener. Таким образом, мой код ожидает, пока поток пользовательского интерфейса завершит работу и снимет "блокировку".
Все это выполняется внутри фрагмента (Activity возвращает родительский объект Activity).
Semaphore semaphore = new Semaphore(1, 1);
semaphore.WaitOne();
Activity?.RunOnUiThread(() =>
{
leaderboard.SetAdapter(adapter);
semaphore.Release();
});
semaphore.WaitOne();
scrollListener = new LazyLoadScrollListener(this, (LinearLayoutManager)layoutManager);
leaderboard.SetOnScrollListener(scrollListener);
semaphore.Release();
Надеюсь, это кому-нибудь поможет.
Ответ 7
Используйте класс AsyncTask, его методы onPostExecure и onProgressUpdate выполняются потоком MAIN (поток пользовательского интерфейса).
http://developer.android.com/reference/android/os/AsyncTask.html
Это лучший способ для вашего дела!
Надеюсь, это поможет вам.