【问题标题】:how to wait for Android runOnUiThread to be finished?如何等待 Android runOnUiThread 完成?
【发布时间】:2011-08-25 05:02:01
【问题描述】:

我有一个工作线程,它创建一个可运行对象并在其上调用 runOnUiThread,因为它处理视图和控件。我想立即使用可运行对象的工作结果。我如何等待它完成?如果它被阻塞,它不会打扰我。

【问题讨论】:

    标签: java android multithreading


    【解决方案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 永远不会结束。
        • 好的,这是一个重要的规范...在这种情况下,您必须使用安德鲁回复技术手动同步线程。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-02-02
        • 1970-01-01
        • 2021-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多