如何等待 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&lt;T&gt;,它的好处是其他人已经为您处理了所有潜在的并发问题:

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(Kotlin) - 如何等待异步任务完成?

如何在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)。