Android上的任务队列就像iOS上的GCD一样?

时间:2011-07-08 10:36:42

标签: android ios multithreading grand-central-dispatch

Android上有任务队列这样的东西吗?我知道它可以手工编写,但是有一个可以使用的库吗?

6 个答案:

答案 0 :(得分:9)

我不确定是否会有这个库,因为Android已经为你想要实现的目标提供了高级构建块。

处理程序

如果我理解正确,您希望从任何线程发布任务,以便在专用线程上逐行排队并执行。这就是Android Handler的用途。

Handler,Looper和MessageQueue的关键特征

  • 处理程序与单个Looper绑定。
  • 每个Looper都有一个关联的MessageQueue
  • 处理程序使用下面的Looper以线程安全的方式将邮件排入队列并将其出列到Looper的{​​{1}}。
  • 处理程序对象本质上是线程安全的,因此可以安全地传递给其他线程。
  • 您可以将多个MessageQueue个对象绑定到同一个Handler。如果要使用不同的处理程序处理不同类型的消息,这非常有用。在这种情况下,您可以保证只有一个处理程序将处理给定Looper的Message / Runnable。 Looper负责将消息发送到右侧Looper
  • 如果您已经熟悉Message Queue范例,可以在2个线程(或类似的Handler的缓冲通道模式)之间进行通信,那么golang只是一个高级类,可以让您使用它模式很容易。

使用Handler发送/接收消息的示例,发布Runnables

Handler

与上述示例的偏差

  • 虽然我在上面的示例中使用了// 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来电,即LooperLooper.prepare()在线程中运行您自己的消息循环。
  • 正如评论中已经提到的,如果您不打算处理任何消息,则无需对股票Looper.loop()进行子类化。
  • 对于需要接收消息的每个线程,您可以使用Handler轻松地在多个线程之间进行通信。
  • Handler中有一些方法可以在将来安排邮件传递和Runnable执行。

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

的AsyncTask

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

我通常使用AsyncTasks来完成我知道我将长时间使用后台线程的任务(至少很容易> = 100 ms)。我可以想到的一些属于这个类别的例子是Binder IPC,RPC调用,网络调用,后台下载等。

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

事实上,Handler可以与AsyncTask结合使用,将工作分成快速部分(由Handler处理)和缓慢部分(由{{1照顾) }})。

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

答案 1 :(得分:3)

我不知道iOS所以我不确定它是否相同,但在Android中你有ScheduledThreadPoolExecutor

答案 2 :(得分:3)

我也在寻找像GCD for Android这样的东西。虽然处理程序和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排队。功能将在实施时添加。

答案 3 :(得分:1)

对于现在找到此主题的任何人,都有一个名为Bolts的新框架。它有任务和延续,可以等待多个任务完成,如GCD。

答案 4 :(得分: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 */
                                        }
                                    });

答案 5 :(得分:0)

你应该检查Handler&amp;弯针

处理程序,默认情况下(*),如dispatch_get_main_queue(),您可以发布任何代码块(Runnable实例)。使用Context.runOnUiThread()和View.post(Runnable)也可以获得相同的方法

(*)Handler的默认构造函数继承当前线程的Looper实例(iOS中的RunLoop)和队列(通过handlerInstace.post ...()方法)Looper上的Runnable实例。

更多提前使用。您可以创建自己的Looper实例(请注意它有点棘手:))。这仍然可能很方便......

另外,对于更高级的使用,处理程序是我在Android上遇到的最好的工具(是的,我想念它们在iOS上),用于在应用程序内部进行消息传递(我猜是进程间通信)。他们可能会被定制来处理发布的消息,bla,bla ......