【问题标题】:Waiting for a Runnable to complete before running another Runnable在运行另一个 Runnable 之前等待 Runnable 完成
【发布时间】:2018-12-11 03:07:36
【问题描述】:

我有一个带有主选项卡活动的 Android 应用,以及各个选项卡中的多个活动。在我的主要活动的 onCreate() 中,我有一个创建列表的可运行文件,在各个活动中,我使用了这个列表。

在单个活动的 onCreate() 中,我还有对列表进行操作的 Runnables。但是,我需要这些 Runnable 仅在主选项卡活动的 Runnable 完成创建列表时运行,否则我会得到一个空列表。我试图找到一种优雅的方式来做到这一点。现在,在我的主要活动的 Runnable 中,我正在设置一个全局布尔变量 isDone,而在我的个人活动的 Runnable 中,我正在等待通过 while 循环设置 isDone。这可行,但可能不是最好的方法。

有什么想法吗?

谢谢。

编辑: 我正在尝试以下代码,但出现运行时错误:

在我的 MainActivity 的 Runnable 中:

mainRunnable = new Runnable() {
  public void run() {
    try {
      generateList();
      synchronized(this) {
      listDone = true;
      notifyAll();
    }
  } catch (Exception e) {
      Log.e("BACKGROUND_PROC", e.getMessage());
    }
  }
};
Thread thread = new Thread(null, mainRunnable, "Background");
thread.start();

在我的 OtherActivity 的 Runnable 中:

otherRunnable = new Runnable() {
  public void run() {
    synchronized(MainActivity.mainRunnable) {
      if (!MainActivity.getListDone()) {
        try {
          wait();
        } catch (InterruptedException e) {
        }
      }
    }
  }
};
Thread thread = new Thread(null, otherRunnable, "Background");
thread.start();

mainRunnable 似乎完全运行,但 otherRunnable 似乎导致应用程序崩溃。我收到以下错误消息:

01-10 15:41:25.543: E/WindowManager(7074): Activity com.myapp.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@40539850 that was originally added here
01-10 15:41:25.543: E/WindowManager(7074): android.view.WindowLeaked: Activity com.myapp.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@40539850 that was originally added here

【问题讨论】:

    标签: java android


    【解决方案1】:

    您可以使用waitnotify 方法。

    为此,需要有一些全局可访问的对象,此时程序中的其他任何东西都没有使用它的锁。我假设创建列表的Runnable 本身可以扮演这个角色。

    所以你可以在创建列表的Runnable 类中添加这样的内容:

    private boolean listsDone = false;
    
    boolean getListsDone() {
        return listsDone;
    }
    

    在创建列表后立即对其run() 方法进行类似的处理:

    synchronized (this) {
        listsDone = true;
        notifyAll();
    }
    

    对于其他 Runnables' run() 方法,在他们需要等待的地方也有类似的情况:

    synchronized (listCreatingRunnableObject) {
        if (!listCreatingRunnableObject.getListsDone()) {
            try {
                listCreatingRunnableObject.wait();
            } catch (InterruptedException e) {
                // handle it somehow
            }
        }
    }
    

    更新:澄清一下,两个synchronized 块需要在同一个对象上同步,并且您必须在该对象上调用wait()notifyAll()。如果对象是Runnable,那么对于第一个可以是隐式的(如上面的代码),但如果是活动,则需要在两种情况下都显式使用活动对象。

    【讨论】:

    • 谢谢。 listCreatingRunnableObject 究竟是什么?是activity类还是Runnable?
    • @user1118764 我假设它是 Runnable,但它可以是任何全局可访问的对象。
    • 当我尝试在其他 Runnable 的 run() 方法中使用您的代码时出现以下错误:01-10 15:24:27.003: W/dalvikvm(5857): threadid=10: thread以未捕获的异常退出 (group=0x4001d5a0) 01-10 15:24:27.023: E/AndroidRuntime(5857): FATAL EXCEPTION: Background 01-10 15:24:27.023: E/AndroidRuntime(5857): java.lang.IllegalMonitorStateException : 在 wait() 之前对象没有被线程锁定
    • @user1118764 哦!我忘记了您必须在用于同步的对象上调用wait()。查看我的答案的更新。
    • @user1118764 我很困惑。你说只有一个列表,所以如果它已经创建,那么没有什么可等待的,你可以立即做接下来的任何事情(这就是为什么if 子句存在的原因,除了防止死锁)。这就是你想要的,对吧?
    【解决方案2】:

    您可以像这样使用Queue

    public class RunQueue implemements Runnable
    {
      private List<Runnable> list = new ArrayList<Runnable>();
    
      public void queue(Runnable task)
      {
        list.add(task);
      }
    
      public void run()
      {
        while(list.size() > 0)
        {
          Runnable task = list.get(0);
    
          list.remove(0);
          task.run();
        } 
      }
    }
    

    这允许您使用一个线程而不是多个线程。而且您可以维护所有现有的“可运行”对象,同时清理它们为等待和加入而拥有的任何代码。

    【讨论】:

    • 我试图做同样的事情,我想过这样做,但不认为它会像我想要的那样工作。看到这个答案,我试了一下,效果很好。到目前为止,实现这一目标的最简单方法。干杯!
    • 有没有办法让这个循环运行?我希望一个线程一遍又一遍地运行(无限地),但它们不能叠加。 IT 必须一次运行一个。 @64BitBob
    • @Riptyde4 - 只需将 while/remove 替换为 for(int i=0; i
    • 不应该是 task.run() 吗?
    • Runnable task = list.get(0); 似乎给出了关于不兼容类型的错误。 list.get() 的结果需要先转换为 Runnable。除此之外,这似乎工作得很好!
    【解决方案3】:

    在主线程中设置一个值为 1 的 CountDownLatch,然后让相关线程等待它。当主线程完成后,你将闩锁倒数到 0,然后等待者将立即启动。

    【讨论】:

    • 谢谢,CountDownLatch 拯救了我的一天。
    【解决方案4】:

    使用while 循环的主动等待根本不是一个好主意。最简单的事情是让第一个 Runnable 作为最后一步启动其余的 Runnable。如果由于某种原因这无法正常工作,请查看将消息发布到处理程序。

    【讨论】:

    • 谢谢。我采用了第一种方法,它奏效了,但这似乎也不是一个好主意。我现在正在尝试通过等待/通知方法来做到这一点。
    【解决方案5】:

    您使用Runnables 而不是Threads 有什么原因吗?如果您使用Threads,您可以使用正是出于这个原因而存在的各种线程通信原语(尤其是wait()join())。

    【讨论】:

    • 没有任何原因,除了我使用的示例代码声明了一个 Runnable myRunnable,然后通过调用来运行它: Thread thread = new Thread(null, myRunnable, "background"); thread.start 不确定这是 Runnable 还是线程?
    • @user1118764 您可能仍然可以通过这种方式直接访问 Thread 对象。但是,如果您使用wait()(正如我在回答中所建议的那样),则不需要,如果您想等待有问题的线程完全执行,您只想使用join(),而不是只需完成一些特定的任务。
    • 如果你使用new Thread(),那么你正在处理一个线程。所以你可以继续使用我提到的通信机制(Taymon 在这个答案中详细描述了 - stackoverflow.com/a/8799584/570930
    【解决方案6】:

    我创建了一个辅助方法,其中包含所有样板代码,用于发布可运行文件并等待它完成运行。

    逻辑与@Taymon 描述的类似,但实现更通用。

    检查一下: https://gist.github.com/Petrakeas/ce745536d8cbae0f0761

    【讨论】:

      【解决方案7】:

      也许你可以参考Android中的Looper。简单地说,一个thead 在while 循环中继续从队列中运行任务。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-29
        相关资源
        最近更新 更多