Подтвердить что ты не робот

Очередь задач на Android, как в GCD на iOS?

Есть ли такая вещь, как очередь задач на Android? Я знаю, что это может быть написано вручную, но есть ли готовая к использованию библиотека для этого?

4b9b3361

Ответ 1

Я не уверен, будет ли библиотека для этого, поскольку Android уже предоставляет высокоуровневые строительные блоки для того, чего вы пытаетесь достичь.

Обработчик

Если вы правильно поняли, вы хотите публиковать задания из любого потока, который должен быть поставлен в очередь и выполняться один за другим в отдельном потоке. Это очень то, на что рассчитан Android Handler.

Основные черты обработчика, Looper и MessageQueue

  • Обработчик привязан к одному Looper.
  • Каждый Looper имеет связанный MessageQueue
  • Обработчик использует Looper снизу для размещения и удаления сообщений в потоковом режиме в Looper MessageQueue.
  • Объекты обработчика по сути являются потокобезопасными и, следовательно, могут безопасно передаваться другим потокам.
  • У вас может быть несколько объектов Handler, привязанных к одному и тому же Looper. Это полезно, если вы хотите обрабатывать различные типы сообщений с помощью разных обработчиков. В этом случае вам гарантируется, что только один из обработчиков обработает Message/Runnable для данного Looper. Looper позаботится о том, чтобы отправить сообщение вправо Handler.
  • Если вы уже знакомы с парадигмой очереди сообщений для связи между двумя потоками (или аналогичным шаблоном буферизованного канала golang), Handler - это просто класс высокого уровня, который позволяет легко использовать этот шаблон.

Пример использования Handler для отправки/получения сообщений, post Runnables

// 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 для управления событиями жизненного цикла компонентов (onPause, onResume и т.д.).

AsyncTask

AsyncTask - еще одна альтернатива планированию задач в другом потоке., Я не буду вдаваться в подробности его реализации, поскольку документация разработчика Android уже подробно описывает его.

Я обычно использую AsyncTasks для задач, которые, как я знаю, я буду использовать фоновый поток в течение длительного времени (легко >= 100 мс, по крайней мере). Некоторые примеры, которые относятся к этой категории, я могу думать о Binder IPC, RPC-вызовах, сетевых вызовах, загрузке фонограмм и т.д.

С другой стороны, Handler более адаптирован для ситуаций, сфокусированных на обработке большего количества сообщений как можно быстрее. Другими словами, избегайте выполнения какой-либо операции блокировки в handleMessage(). Вы можете легко писать код без блокировки, используя Handler, он управляет всей блокировкой для вас при вводе и удалении сообщений.

Фактически AsyncTask можно использовать в сочетании с Handler, разбивая работу на быструю часть (заботясь о Handler) и медленной части (позаботится AsyncTask).

PS: Хотя касательно вопроса, если вас интересует парадигма очереди сообщений; посмотрите LMAX Disruptor, которая является высокопроизводительной межпоточной библиотекой очереди сообщений. Их проектный документ очень хорошо объясняет, в каких частях очереди сообщений требуется блокировка/атомный доступ.

Ответ 2

Я не знаю iOS, поэтому я не уверен, что это то же самое, но в Android у вас есть ScheduledThreadPoolExecutor

Ответ 3

Я также искал что-то вроде GCD для Android. В то время как Handlers и AsyncTasks являются прекрасными, красота GCD (по моему скромному мнению) заключается в том, что вы можете отправить нагрузку на фоновый поток, чтобы сделать тяжелый подъем. Когда выполнение выполнено, я легко выполняю обновления пользовательского интерфейса в потоке пользовательского интерфейса.

Так как я ничего не нашел, я и мой школьный помощник решили создать свою собственную. Вы можете найти его по адресу: ICDispatch on github

В основном все, что вам нужно сделать, это объявить класс приложения, расширяющий ICDispatchApplication вместо приложения, и когда вы хотите отправить что-то, что вы просто вызываете

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.
          }
      }
   }
});

Хуже всего то, что будет много отступов. Чтобы свести к минимуму отступ, вы можете использовать лямбда-нотацию:

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. Функции будут добавлены по мере их реализации.

Ответ 4

Для тех, кто сейчас находит этот поток, есть новая структура, доступная под названием Bolts. Он имеет задачи и продолжения и может ждать выполнения нескольких задач, таких как GCD.

Ответ 5

Я беру этот образец из 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 */
                                        }
                                    });

Ответ 6

Вы должны проверить Handler и Loopers

Обработчики, по умолчанию (*), например dispatch_get_main_queue(), и вы можете опубликовать любой блок (Runnable instance) кода. Тот же подход также получен с помощью Context.runOnUiThread() и View.post(Runnable)

(*) Конструктор по умолчанию Handler наследует текущий экземпляр Looper потока (RunLoop в iOS) и очереди (с помощью методов handlerInstace.post...()) Runnable экземпляры на Looper.

Для более удобного использования. Вы можете создать свой собственный экземпляр Looper (помните, что это немного сложно:)). Тем не менее это может быть удобно...

Кроме того, для более удобного использования Handlers являются лучшими инструментами, с которыми я сталкиваюсь на Android (и да, я пропустил их на iOS) для обмена сообщениями внутри приложения (межпроцессное общение - это то, что я думаю). Они могут быть настроены для обработки отправленных сообщений, bla, bla...