Android上是否有类似iOS中GCD的任务队列?

20

Android上是否有任务队列这样的东西?我知道它可以手动编写,但是否有现成的库可供使用?

6个回答

10

我不确定是否有这方面的库,因为Android已经提供了高级构建块来实现您要实现的目标。

Handler

如果我理解正确,您想从任何线程中发布任务以便在专用线程上按顺序排队并执行。这正是 Android Handler 的作用。

Handler、Looper 和 MessageQueue 的关键特点

  • 一个 Handler 绑定一个 Looper
  • 每个 Looper 关联一个 MessageQueue
  • Handler 在底层使用一个 Looper,以线程安全的方式将消息排队到 LooperMessageQueue 中进行出队和处理。
  • Handler 对象固有线程安全性,因此可以安全地传递给其他线程。
  • 你可以有多个 Handler 对象绑定到同一 Looper 上。这对于使用不同的 Handler 处理不同类型的消息很有用。在这种情况下,你可以确保只有一个 Handler 会处理给定 Looper 上的消息/Runnable。Looper 负责将 Message 分发到正确的 Handler。
  • 如果你已经熟悉了在两个线程之间通信的 Message Queue 范例(或类似于 golang 的 buffered channel 模式),Handler 就是一个让你轻松使用此模式的高级类。

使用 Handler 发送/接收消息、发布 Runnable 的示例

// BEGIN One-time Initialization
// Create a Handler thread
// This provides the looper for the Message Queue and
// will be processing all your messages (i.e. tasks).
handlerThread = new HandlerThread("SomeThreadName");

// Start the Handler Thread
// The thread will block (using the looper) until it
// receives a new message
handlerThread.start();

// Create a Message Handler which you can use to
// post and process messages
// The same Handler can also be used to post a Runnable which will get
// executed on handlerThread
handler = new CustomHandler(mHandlerThread.getLooper());
// END One-time Initialization

// Different ways to post a message to the Handler Thread
// These calls are thread-safe, can be called safely and
// concurrently from multiple threads without race conditions
handler.sendEmptyMessage(MESSAGE_ID_1);
handler.sendEmptyMessage(MESSAGE_ID_2);
handler.sendMessage(handler.obtainMessage(MESSAGE_ID_3, obj1));
handler.sendMessage(handler.obtainMessage(MESSAGE_ID_4, value, obj1));
handler.sendMessage(handler.obtainMessage(MESSAGE_ID_5, value1, valu2, obj1));

// Post a runnable on the Handler Thread
// This is thread-safe as well
// In fact all methods on the Handler class are thread-safe
handler.post(new Runnable() {
    @Override
    public void run() {
        // Code to run on the Handler thread
    }
});

// A skeleton implementation for CustomHandler
// NOTE: You can use the Handler class as-is without sub-classing it, if you
// intend to post just Runnables and NOT any messages
public class CustomHandler extends Handler {
    public CustomHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message message) {
        if (message != null) {
            // Process the message
            // The result can be sent back to the caller using a callback
            // or alternatively, the caller could have passed a Handler
            // argument, which the Handler Thread can post a message to

            switch (message.what) {
                case MESSAGE_ID_1:
                    // Some logic here
                    break;
                case MESSAGE_ID_2:
                    // Some logic here
                    break;
                case MESSAGE_ID_3:
                    // Some logic here
                    break;
                case MESSAGE_ID_4:
                    // Some logic here
                    break;
                case MESSAGE_ID_5:
                    // Some logic here
                    break;
                // Add more message types here as required
            }
        }
    }
}

// After you're done processing all messages and you
// want to exit the Handler Thread
// This will ensure that the queue does not accept any
// new messages, and all enqueued messages do get processed
handlerThread.quitSafely();

与上面示例的偏差

  • 虽然我在上面的示例中使用了HandlerThread,但不强制要求使用它。您甚至可以直接使用Looper调用(即Looper.prepare()Looper.loop()在线程中运行自己的消息循环。
  • 如评论中所述,如果您不打算处理任何消息,则无需子类化默认的Handler
  • 您可以通过为需要接收消息的每个线程使用一个Handler来轻松地在多个线程之间通信。
  • Handler中还有一些方法可用于计划将来的消息传递和Runnable执行。

Android框架在内部广泛使用Handler来管理组件生命周期事件(例如onPauseonResume等)。

异步任务

AsyncTask是另一种在不同线程上调度任务的选择。我不会详细介绍它的实现方式,因为Android开发者文档已经对其进行了详细描述。

我通常使用AsyncTasks来处理长时间运行的后台线程任务(至少轻松地>= 100毫秒)。我能想到的一些属于这个类别的例子是Binder IPC、RPC调用、网络调用、后台下载等。

另一方面,Handler更适合于专注于尽可能快地处理更多数量的消息的情况。换句话说,避免在handleMessage()中执行任何阻塞操作。您可以使用Handler轻松编写无锁代码,它在排队和出队消息时管理所有锁定。

实际上,AsyncTask 可以与Handler结合使用,通过将工作分成快速部分(由Handler处理)和慢速部分(由AsyncTask处理)。

附:虽然与问题无关,但如果您对消息队列范例感兴趣,请查看LMAX Disruptor,这是一个高性能的跨线程消息队列库。他们的设计文档很好地解释了消息队列的哪些部分需要锁定/原子访问。


“并在专用线程上逐一执行。” GCD不同于其他队列,它使用专用线程池而非单个线程来执行队列中的任务。这是一个关键的区别,因为在GCD中,您可以创建许多队列而不必成比例地增加线程数。 - Michael Bishop
所有这些都是Java代码,而不是NDK代码。此外,GDC具有比任何Linux系统更好的内核支持,因为它将任务队列(而不是线程)移动到内核调度程序中。 - Lothar

4

我也曾经寻找类似于GCD的东西来用在Android上。虽然Handlers和AsyncTasks也很棒,但是我认为GCD的精髓在于可以将工作负载派发到后台线程进行处理,当执行完成后,轻松地在UI线程上执行UI更新。

由于我没有找到任何现成的解决方案,所以我和我的学校同伴决定自己创建一个。你可以在以下链接找到它:

ICDispatch on github

基本上,你只需要声明一个继承自ICDispatchApplication而不是Application的Application类,当你想要调度某些任务时,只需调用以下命令:

App.executeOn(int queue, ICBlock block);

例如:

App.executeOn(ICDispatch.NORMAL, new ICBlock(){
   public void run(){
      //do stuff...
      App.executeOn(ICDispatch.MAIN, new ICBlock(){
          public void run(){
             //post result to UI thread.
          }
      }
   }
});

最糟糕的部分是会有很多缩进。为了最小化缩进,您可以使用lambda符号:
App.executeOn(ICDispatch.NORMAL, ()->{
    //do stuff...
    //do some more...
    //then even more
    App.executeOn(ICDispatch.MAIN,() -> {
       //Post result on UI thread.
    }
});

目前ICDispatch支持LOW、NORMAL、HIGH、MAIN和CONCURRENT队列。随着实现的功能,将添加更多特性。


要求采用者扩展ICDispatchApplication是一个糟糕的决定,如果更多的库需要这样做,那么您将无法使用其中的一个或其他... - Renetik
我同意。我不建议任何人使用这个库,因为它是为Android 2.2制作的。但是,如果你仍然想这样做,你只需要创建一个ICDispatch实例并调用它的initICDispatch方法。然后你就可以摆脱扩展应用程序的依赖了(请参阅https://github.com/johanrisch/ICDispatch/blob/master/ICDispatch/src/ICDispatch/ICDispatchApplication.java)。 - Risch

3

1
我从Telegram Code中提取了这个示例:
你可以声明扩展线程来实现这种方法。
public static volatile DispatchQueue globalQueue = new DispatchQueue("globalQueue");

这个类是:

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import java.util.concurrent.CountDownLatch;

public class DispatchQueue extends Thread {

    private volatile Handler handler = null;
    private CountDownLatch syncLatch = new CountDownLatch(1);

    public DispatchQueue(final String threadName) {
        setName(threadName);
        start();
    }

    private void sendMessage(Message msg, int delay) {
        try {
            syncLatch.await();
            if (delay <= 0) {
                handler.sendMessage(msg);
            } else {
                handler.sendMessageDelayed(msg, delay);
            }
        } catch (Exception e) {
            FileLog.e("tmessages", e);
        }
    }

    public void cancelRunnable(Runnable runnable) {
        try {
            syncLatch.await();
            handler.removeCallbacks(runnable);
        } catch (Exception e) {
            FileLog.e("tmessages", e);
        }
    }

    public void postRunnable(Runnable runnable) {
        postRunnable(runnable, 0);
    }

    public void postRunnable(Runnable runnable, long delay) {
        try {
            syncLatch.await();
            if (delay <= 0) {
                handler.post(runnable);
            } else {
                handler.postDelayed(runnable, delay);
            }
        } catch (Exception e) {
            FileLog.e("tmessages", e);
        }
    }

    public void cleanupQueue() {
        try {
            syncLatch.await();
            handler.removeCallbacksAndMessages(null);
        } catch (Exception e) {
            FileLog.e("tmessages", e);
        }
    }

    @Override
    public void run() {
        Looper.prepare();
        handler = new Handler();
        syncLatch.countDown();
        Looper.loop();
    }
}

以及调用者:

globalQueue.postRunnable(new Runnable() {
                                        @Override
                                        public void run() {
                                            /* do here what you want */
                                        }
                                    });

1

对于现在发现这个帖子的任何人,现在有一个名为Bolts的新框架可用。它具有任务和继续功能,并且可以等待多个任务完成,就像GCD一样。


0

你应该检查 Handler 和 Looper

Handlers 默认使用 dispatch_get_main_queue(),你可以发布任何代码块(Runnable 实例)。同样的方法也可以通过 Context.runOnUiThread() 和 View.post(Runnable) 实现。

默认构造函数将继承当前线程的 Looper 实例 (RunLoop 在 iOS 中),并在 Looper 上排队 Runnable 实例 (通过 handlerInstace.post...() 方法)。

对于更复杂的用法,你可以创建自己的 Looper 实例 (请注意,这有点棘手 :))。但这可能会很方便...

此外,对于 Android 我发现 Handlers 是最好的工具之一 (我在 iOS 上也很想念它们),用于应用程序内部的消息传递 (我猜这是进程间通信)。可以自定义处理已发布的消息,等等...


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