在后台线程运行 Handler 消息

43

我想在后台线程中运行一些Runnable,我想使用Handler因为它方便进行延迟。

我的意思是

handler.post(runnable, delay);

在< strong >后台线程中应该运行< strong >可运行项。 是否可以创建这样的处理程序? 是否存在某个“后台”循环器或如何创建它?

附言:我知道如何使用自定义类扩展Thread来完成它,但与使用Handler的方法相比,它需要更多的编码工作。因此,请不要发布其他解决方案或类似内容。

handler.post(new Runnable() {
    @Override
    public void run() {
        new Thread() {
            @Override
            public void run() {
                //action
            }
        }.start();
    }
});

我只是想知道 Handler 是否可以以“干净”的方式完成它。


你不能从后台线程运行其他线程。你只能从主线程中执行此操作。因此,如果你想要运行线程,可以使用以下方法:getActivity().runOnUiThread(new Runnable() { .... 在这里创建新的处理程序。 - Dariusz Mazur
@daro2189 我想从主线程中延迟地运行一个后台线程。我知道 Handler 可以接受任何拥有 Looper 的线程。但我不确定是否可以在后台线程中创建一个 Looper。 - Yaroslav Mytkalyk
5个回答

86

你可以简单地这样做:

private Handler mHandler;

private HandlerThread mHandlerThread;

public void startHandlerThread(){
    mHandlerThread = new HandlerThread("HandlerThread");
    mHandlerThread.start();
    mHandler = new Handler(mHandlerThread.getLooper());
}

然后使用以下命令调用:

mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
          // Your task goes here
        }
    },1000);

4
增加正确的顺序来停止所有事情将非常有帮助。 - Igor Mikushkin
2
如果我想在run方法中更新UI,我需要调用runOnUiThread吗? - David
为了避免内存泄漏,可以使用WeakHandler代替Handler; - Javad Asoodeh

10

你可以尝试像这样做

    private void createHandler() {
        Thread thread = new Thread() {
            public void run() {
                Looper.prepare();

                final Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                       // Do Work
                        handler.removeCallbacks(this);
                        Looper.myLooper().quit();
                   }
                }, 2000);

                Looper.loop();
            }
        };
        thread.start();
    }

1
这个想法与我在Yuichi Araki指引我正确方向后所做的类似。当然,Handler应该被分配给一个字段。 - Yaroslav Mytkalyk
是的,只是一个完整的例子,将所有内容汇集在一起。 - Ayman Mahgoub
在所有事情完成后,我们是否也应该清理线程本身? - Karan

5

那似乎是我想要的。我在同一份文档中查找方法,但不知何故错过了文档标题中的示例 xD。 - Yaroslav Mytkalyk
关于[循环者和处理程序]的好文章 http://mindtherobot.com/blog/159/android-guts-intro-to-loopers-and-handlers/ - StepanM

0

不清楚你所说的Handler是什么意思。

听起来你需要一个线程,通过队列接收要执行的进程。您可能会从调查Executor 这里中受益,但以下是一对通过队列通信的简单双线程。

public class TwoThreads {
  public static void main(String args[]) throws InterruptedException {
    System.out.println("TwoThreads:Test");
    new TwoThreads().test();
  }
  // The end of the list.
  private static final Integer End = -1;

  static class Producer implements Runnable {
    final Queue<Integer> queue;

    public Producer(Queue<Integer> queue) {
      this.queue = queue;
    }

    @Override
    public void run() {
      try {
        for (int i = 0; i < 1000; i++) {
          queue.add(i);
          Thread.sleep(1);
        }
        // Finish the queue.
        queue.add(End);
      } catch (InterruptedException ex) {
        // Just exit.
      }
    }
  }

  static class Consumer implements Runnable {
    final Queue<Integer> queue;

    public Consumer(Queue<Integer> queue) {
      this.queue = queue;
    }

    @Override
    public void run() {
      boolean ended = false;
      while (!ended) {
        Integer i = queue.poll();
        if (i != null) {
          ended = i == End;
          System.out.println(i);
        }
      }
    }
  }

  public void test() throws InterruptedException {
    Queue<Integer> queue = new LinkedBlockingQueue<>();
    Thread pt = new Thread(new Producer(queue));
    Thread ct = new Thread(new Consumer(queue));
    // Start it all going.
    pt.start();
    ct.start();
    // Wait for it to finish.
    pt.join();
    ct.join();
  }
}

谢谢回答。执行器不适合,因为只有一个线程必须在延迟后启动,但如果在延迟结束之前发生某些条件,则必须取消。 Handler非常适合此操作,但我想在后台运行Runnable,而不是在默认情况下Handler运行的主线程上运行。 - Yaroslav Mytkalyk

0
我在 Kotlin 中实现了一种简单的在后台线程上运行任务的方法:
fun background(function: () -> Unit) = handler.post(function)

private val handler: Handler by lazy { Handler(handlerThread.looper) }

private val handlerThread: HandlerThread by lazy {
    HandlerThread("RenetikBackgroundThread").apply {
        setUncaughtExceptionHandler { _, e -> later { throw RuntimeException(e) } }
        start()
    }
}

一般的想法是任务执行简单,依次顺序运行,并且未捕获的异常会传播到主线程,以免丢失。 Later 函数基本上是在主线程上运行的处理程序。
因此,您可以像这样简单地发布任务:
 background {
    some task to do in background...  
 }

一些代码

 background {
    other task to do in background...  
    later {
        on main thread when all tasks are finished...
    }
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接