如何等待 Android runOnUiThread 完成?
Posted
技术标签:
【中文标题】如何等待 Android runOnUiThread 完成?【英文标题】:how to wait for Android runOnUiThread to be finished? 【发布时间】:2011-08-25 05:02:01 【问题描述】:我有一个工作线程,它创建一个可运行对象并在其上调用 runOnUiThread,因为它处理视图和控件。我想立即使用可运行对象的工作结果。我如何等待它完成?如果它被阻塞,它不会打扰我。
【问题讨论】:
【参考方案1】:这就是我使用 Kotlin 扩展的方式
添加扩展功能以检查线程并在UI线程上运行
fun Context.executeOnUIThreadSync(task: FutureTask<Boolean>)
if (Looper.myLooper() == Looper.getMainLooper())
task.run()
else
Handler(this.mainLooper).post
task.run()
它会阻塞UI线程并等待返回
fun checkViewShown(): Boolean
val callable: Callable<Boolean> = Callable<Boolean>
// Do your task in UI thread here
myView != null && myView?.parent != null
val task: FutureTask<Boolean> = FutureTask(callable)
applicationContext.executeOnUIThreadSync(task)
return task.get()
【讨论】:
【参考方案2】:如果有人在 Xamarin 应用程序中开发时遇到这个问题,我把我的 C# 代码留在这里。
我的 RecyclerView 适配器设置得太晚,所以它在我的 ScrollListener 构造函数中返回 null。这样我的代码等待 ui 线程完成工作并释放“锁”。
所有这些都在 Fragment 中运行(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();
希望对某人有所帮助。
【讨论】:
【参考方案3】:一种解决方案可能是利用 Java 的 FutureTask<T>
,它的好处是其他人已经为您处理了所有潜在的并发问题:
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
替换为其他内容来从主线程返回结果。
【讨论】:
我真的很喜欢这个答案,但是我在将静态调用的上下文(使用 JNI)使用到当前实例时遇到了麻烦,不得不将 runnable 声明为最终静态,并通过以下方式进行所有并发管理手和活套检查(我希望有更好的方法......我应该问这样......),但我非常喜欢这种方法。【参考方案4】:我认为最简单的方法是使用“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
(我相信这个问题与“How to make timer task to wait till runOnUiThread completed”重复)
【讨论】:
我真的很喜欢这种方法。它很干净......现在如果只有一种方法可以将闩锁注入匿名可运行文件。【参考方案5】:只是勾勒出亮点
synchronized( myRunnable )
activity.runOnUiThread(myRunnable) ;
myRunnable.wait() ; // unlocks myRunable while waiting
同时...在 myRunnable...
void run()
// do stuff
synchronized(this)
this.notify();
【讨论】:
run() 是否也需要同步? 尝试实现,但我的应用程序在尝试 notify() 时崩溃...为什么?没有例外,只是一次普通的崩溃 我认为您需要将notify()
包装在同步块中,否则对notify()
的调用可能会在对wait()
的调用之前发生
Baqueta 的建议使我的代码正常工作。否则,它会因“同步对象未在通知前被线程锁定”异常而崩溃。
@Baqueta 在myRunnable.run()
方法中,我将this.notify()
包装到一个同步块中,正如你所建议的。但它不起作用,所以在wait()
之前调用了notify()
。我不明白...我使用synchronized(this)
。这不对吗?【参考方案6】:
也许有点简单,但互斥体可以完成这项工作:
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();
【讨论】:
在单元测试中你应该使用Instrumentation.runOnMainSync
- “在应用程序的主线程上执行一个调用,阻塞直到它完成。”如果您有兴趣自己复制代码,请查看 Instrumentation 中 SyncRunnable 类的代码。
错了。您描述的顺序正是应该发生的事情,这不会陷入僵局。 release
将许可计数从 0 增加到 1。因此 acquire
可以获得许可并将计数再次减少到 0。【参考方案7】:
安德鲁的回答很好,我创建了一个更容易使用的类。
接口实现:
/**
* 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();
【讨论】:
这是一个很好的答案,但我认为它不处理的一种情况是它是从 UI 线程调用的。不过应该很容易检测到这种情况并解决它。 @Baqueta - 从 UI 线程调用时它仍然可以工作,但您可以通过避免锁定来提高性能: if ( Looper.myLooper() == Looper.getMainLooper() ) uiRunnable 。跑步();返回; 注意:当使用这种方法在主 UI 中运行我的本机库调用时,这个线程出了点问题,pthread_join()
调用永远挂起。不过不知道为什么。【参考方案8】:
使用 AsyncTask 类,其方法 onPostExecure 和 onProgressUpdate 由 MAIN 线程(UI 线程)执行。
http://developer.android.com/reference/android/os/AsyncTask.html
是更好的方法!
希望对你有帮助。
【讨论】:
嗨 pedr0。感谢您的回答,但我不确定它是否适合我。从文档来看,AsyncTask 似乎是用于从 ui 线程创建任务以在工作线程中运行。我的情况正好相反:我在一个工作线程上,为 ui 线程创建一个可运行对象,然后等待(仍在工作线程上)可运行对象完成。除了我的情况,还有其他类似 AsyncTask 的东西吗? 但是当 onPostExecute 方法被执行时,你的工作线程已经完成了!您可以向您的活动应用程序发送消息,以做您想做的一切! Asynctask 是更好的方法.. 我认为你应该尝试一些这样的教程:xoriant.com/blog/mobile-application-development/… 模式是 Activity-Handler-AsyncTask ,你会解决你的问题(我希望!!) 你误解了这个问题。代码是从工作线程中调用的,向活动发送一个可运行的,而不是相反。工作线程,即 GLSurface.Renderer 永远不会结束。 好的,这是一个重要的规范...在这种情况下,您必须使用安德鲁回复技术手动同步线程。以上是关于如何等待 Android runOnUiThread 完成?的主要内容,如果未能解决你的问题,请参考以下文章
如何在Android java插件端等待异步操作(任何I / O)?
如何等待 10 秒而不锁定 android 中的应用程序 UI [重复]
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。