【问题标题】:Android: Forcing the main run loop to run before current thread of execution is completeAndroid:强制主运行循环在当前执行线程完成之前运行
【发布时间】:2014-07-31 15:10:42
【问题描述】:

在 iOS 上,如果我希望我当前的执行线程等待(即阻塞)并且主循环运行以便主队列中的下一个执行线程可以执行,我调用:

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];

我将如何在 Android 上做同样的事情?

【问题讨论】:

    标签: android ios runloop


    【解决方案1】:

    这在 Android 中确实可以做到。沙查尔的答案是正确的。问题不在于主循环会阻塞(除非代码是在主线程上执行的,但这不是问题所提出的)。问题是另一个线程没有阻塞,而只是在while循环中循环和燃烧CPU周期。这是我在应用程序中使用的主要方法的阻塞运行:

    /**
     * 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 发送到绑定到主线程的Looper 的Handler。在发布Runnable 参数之后,我们再发布另一个Runnable,它将在Runnable 参数完成执行后执行,因为Handler 执行发布的Messages 和Runnables 是按顺序发布的。第二个Runnable 用于通知阻塞线程,主线程上的工作已经完成。

    在发布第二个Runnable 后,我们现在阻止后台线程并等待我们收到通知。同步在lock 上执行的操作很重要,这样操作在每个线程上都是原子的。

    后台线程调用监视器上的wait() 并等待直到mRunComplete == true。如果它得到一个InterruptedException,重要的是继续等待并在我们完成后恢复线程的中断状态,因为我们自己并没有使用中断机制来取消我们的任务,恢复它允许另一个更高的方法调用堆栈来处理中断。 See "Dealing with InterruptedException".

    当Runnable 参数已完成执行并且第二个发布的Runnable 执行时,它只需将mRunComplete 设置为true 并通知被阻塞的线程继续执行,找到mRunComplete == true 现在从blockingRunOnMain() 返回,在主 UI 线程上同步执行了Runnable 参数。

    【讨论】:

      【解决方案2】:

      一个简短的解决方法是设置一个由下一个主线程循环更改的布尔值。

      在主线程上运行可以通过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) {
          ;
      }
      

      【讨论】:

      • 您的解决方案不起作用。主循环将在到达while 循环的那一刻阻塞,然后我们得到一个无限循环。
      【解决方案3】:

      很抱歉让您失望了,但在 Android 中无法满足您的要求。

      【讨论】:

      猜你喜欢
      • 2021-11-16
      • 2020-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多