Запуск различных методов в фоновом потоке без дублирования

В моем приложении Android у меня есть количество методов, которые должны выполняться в фоновом потоке.

Один из методов выглядит следующим образом:

public static void processItem(final Context context, final String itemId)
{
    if (Looper.myLooper() == Looper.getMainLooper())
    {
        Thread task = new Thread()
        {
            @Override
            public void run()
            {
                processItem(context, playlistId);
            }
        };

        task.start();
        return;
    }

    // actual processing
}

Как вы можете видеть, существует некоторый код, который обеспечивает только подходящую нить.

Как избежать повторного копирования этого «потокового» кода для каждого метода, который должен выполняться в фоновом потоке?

11 голосов | спросил Bobrovsky 8 J000000Tuesday14 2014, 15:31:40

3 ответа


16

Расширение Thread - это «анти-шаблон» в Java. Правильный способ выполнения этой работы - создать экземпляр Runnable и использовать runnable как конструктор для экземпляра Thread.

Thread t = new Thread(new Runnable() {
    public void run() {
        /*
         * Do something
         */
    }
});

t.start();

Но, и это большой «но», в Java, в общем, практика создания и использования потоков, подобных этому, заменяется фреймворками, которые лучше управляют потоками. В обычной Java вы должны использовать Executor, ExecutorService и Executors в параллельном пакете.

Что касается конкретных случаев использования для Android, вы должны читать дальше модели потоков в Android , уделяя особое внимание AsyncTask в Android dev набор

Обновление: подробнее об анти-шаблоне потока

Джон Скит говорит так ... ;-) , и большинство людей согласны. У вас есть что-то, что нужно сделать. Это то, что вам нужно реализовать, способ сделать это. Место для запуска что-то в потоке. Вы не реализуете поток, вы выполняете задачу. Здесь есть две разные концепции: что нужно сделать, и где это нужно сделать. Вы реализуете только один из них.

Обновление: альтернативное решение

Чтобы уменьшить логику, необходимую для управления потоком, на котором выполняется код, я бы сделал одну из двух вещей:

  1. просто всегда используйте другой поток. Просто удалите if-условие и выполните всю обработку в методе Runnable.
  2. У вас есть общий метод утилиты, который выглядит примерно так:

    private static final ExecutorService THREADPOOL = Executors.cachedThreadPool();
    
    public static void runButNotOn(Runnable toRun, Thread notOn) {
        if (Thread.currentThread() == notOn) {
            THREADPOOL.submit(toRun);
        } else {
            toRun.run();
        }
    }
    

    , а затем вы можете вызвать его в своем методе с помощью:

    private static void processImplementation(final Context context, final String itemId) {
         // ... the actual work.
    }
    
    public static void processItem(final Context context, final String itemId) {
        Runnable task = new Runnable(){
            public void run() {
                processImplementation(context, itemId);
            }
        };
        runButNotOn(task, Looper.getMainLooper().getThread());
    }
    
ответил rolfl 8 J000000Tuesday14 2014, 18:01:04
4

Вы можете передать Runnable в функцию processItem, которая единственная, что делает это, чтобы убедиться, что Looper такой же, как основной петлитель, и если да, начните поток с обратным вызовом, который вы ему передали.

processItem(Runnable callback);

будет:

public static void processItem(Runnable callback)
{
    if (Looper.myLooper() == Looper.getMainLooper())
    {
        new Thread(callback).start();
    }
}

И когда вы его назовете:

processItem(new Runnable() {
  @Override
  public void run() {
    What you want to do?
  }
});

P.S Без дополнительного контекста /информации мы не можем вам помочь ... И я думаю, что это вопрос для Stackoverflow, но я не уверен.

ответил Marco Acierno 8 J000000Tuesday14 2014, 15:54:33
2

Сделайте шаг назад и посмотрите на свои проектные решения. Вы говорите о «выполнении метода в фоновом потоке». То, что я хотел бы предложить, - это подумать о задаче, которую вы пытаетесь достичь, и создать там логику.

Например, ваш метод processItem может принадлежать классу, который обрабатывает потоки для вас :

/**
 * A class which processes item on a separate thread.
 */
public class ItemProcessor {

    private final Context mContext;
    private final String mItemId;

    public ItemProcessor(final Context context, final String itemId){
        mContext = context.getApplicationContext();
        mItemId = itemId;
    }

    /**
     * Starts processing the item on a separate thread.
     */
    public void process() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                // Do the processing.
            }
        }).start();
    }

}

Подумайте о modules в своем приложении, не просто напишите линейный код. Например, теперь гораздо проще добавить интерфейс обратного вызова в класс, используя метод setter.

ответил nhaarman 8 J000000Tuesday14 2014, 21:24:14

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132