Многократный обратный вызов в Android/Java?

В потоках Java вы можете иметь некоторое количество потоков в списке, запускать их и иметь основной поток join один, затем другой, проходя и ожидая завершения всех процессов, прежде чем двигаться дальше.

В других моделях я не уверен, как бы вы это сделали. Возьмем, к примеру, класс команд RootTools 3.0. Вы создаете Command который имеет три метода, commandOutput, commandFinished, commandTerminated и хотя вы можете использовать обратный вызов, чтобы сделать что-то в конце процесса, я не знаю, как вы будете ждать нескольких процессов (например, просматривая список нескольких каталогов и суммируя размеры файлов).

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

3 ответа

Решение

Вы можете вызвать wait() для команды, которую вы выполняете.

Хотя перед этим вы должны отключить обработчик для команды, которую вы вызываете, подождите. Вы можете сделать это для каждой команды, установив для RootTools.handlerEnabled значение false или используя конструктор в каждой отдельной команде и передав значение false, чтобы отключить обработчик для этой команды.

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

Когда вы выключаете обработчик для команды и вызываете wait(), команда вызовет notifyAll(), когда завершится, чтобы ваш поток возобновился.

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

Вступление

Я прошел эту тему для одного из моих предыдущих проектов и нашел различные решения проблемы (в конце концов я использовал метод 1 для проекта, потому что он лучше всего подходит для него). Я поделюсь ими с вами настолько просто, насколько это возможно. Для лучшего понимания я объясню их на примере загрузки нескольких изображений.

Позволять ImageDownloader быть классом для асинхронной загрузки изображения с URL и имеет следующие свойства.

  • Интерфейс - ImageDownloadCallback чтобы получить обратный вызов, когда задача завершена. У него есть два метода
    • void onSuccess(String imagePath): вызывается, когда задача успешно завершена.
    • void onFailure(): вызывается, когда задача не выполнена.
  • Метод - download(String url, ImageDownloadCallback callback) начать задачу загрузки

PauseModeCallbackHandler, ChainModeCallbackHandler а также ParallelModeCallbackHandler являются классами-обертками для обратных вызовов для трех методов соответственно. Вы можете настроить их в соответствии с тем, какую задачу вы хотите сделать.




Способ 1:

Выполняйте задачи одну за другой, останавливая начальный поток.

Pros
Получает результаты в исходной теме

Cons
Нужно сделать поток ожидания


ThreadLockedTask

Вы можете использовать этот класс, чтобы заставить поток ждать, пока не будет получен результат.

import java.util.concurrent.atomic.AtomicReference;

/**
 * @author Ahamad Anees P.A
 * @version 1.0
 * @param <T> type
 */
public class ThreadLockedTask<T> {

    private AtomicReference<ResultWrapper<T>> mReference;

    public ThreadLockedTask() {
        mReference = new AtomicReference<>(new ResultWrapper<T>());
    }

    public T execute(Runnable runnable) {
        runnable.run();
        if (!mReference.get().mIsSet)
            lockUntilSet();
        return mReference.get().mResult;
    }

    private void lockUntilSet() {
        synchronized (this) {
            while (!mReference.get().isSet()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public void setResult(T result) {
        synchronized (this) {
            ResultWrapper<T> wrapper = mReference.get();
            wrapper.setResult(result);
            wrapper.setIsSet(true);
            notify();
        }
    }

    public static class ResultWrapper<T> {
        private boolean mIsSet;
        private T mResult;

        public boolean isSet() {
            return mIsSet;
        }

        public T getResult() {
            return mResult;
        }

        void setIsSet(boolean isCompleted) {
            this.mIsSet = isCompleted;
        }

        void setResult(T result) {
            this.mResult = result;
        }
    }

}


Образец

import java.util.ArrayList;
import java.util.List;

public class PauseModeCallbackHandler {

    // List of results
    private static List<String> results;

    public static void start(final List<String> urls, final ImageDownloader.ProgressUpdateListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                results = new ArrayList<>();

                // Do tasks one by one
                for (final String url :
                        urls) {

                    //Here the result is a String. Change "String" in the following two lines for other datatypes.
                    final ThreadLockedTask<String> task = new ThreadLockedTask<>();
                    final String imagePath = task.execute(new Runnable() {
                        @Override
                        public void run() {
                            //Start the task here
                            ImageDownloader.getInstance(listener).download(url,
                                    new ImageDownloader.ImageDownloadCallback() {
                                        @Override
                                        public void onSuccess(String imagePath) {
                                            //Set the result on success
                                            task.setResult(imagePath);
                                        }

                                        @Override
                                        public void onFailure() {
                                            //Set result as null on failure
                                            task.setResult(null);
                                        }
                                    });
                        }
                    });

                    if (imagePath!=null)
                        results.add(imagePath);

                }

                afterCallbacks();
            }
        }).start();
    }

    private PauseModeCallbackHandler() {}

    private static void afterCallbacks() {
        // All tasks completed. List "results" now holds the result

        DemoActivity.isTasksInProgress = false;
    }
}




Способ 2:

Выполнять задачи из обратного вызова предыдущего, как цепная реакция.


Образец

import java.util.ArrayList;
import java.util.List;

public class ChainModeCallbackHandler implements ImageDownloader.ImageDownloadCallback {

    // List of args to start the task. Use pojo classes if your task has multiple args
    private static List<String> urls;

    // List of results
    private static List<String> results;

    // Optional.
    private static ImageDownloader.ProgressUpdateListener progressUpdateListener;

    // Leave it as it is
    private int index;

    public static void start(List<String> urls, ImageDownloader.ProgressUpdateListener listener) {
        ChainModeCallbackHandler.urls = urls;
        results = new ArrayList<>();
        progressUpdateListener = listener;

        //Start with the first task
        ImageDownloader.getInstance(listener).download(urls.get(0), new ChainModeCallbackHandler(0));
    }

    private ChainModeCallbackHandler(int index) {
        this.index = index;
    }

    @Override
    public void onSuccess(String imagePath) {
        results.add(imagePath);
        afterCallback();
    }

    @Override
    public void onFailure() {
        afterCallback();
    }

    private void afterCallback() {
        int nextIndex = index+1;
        if (nextIndex<urls.size()) {
            //Tasks are not completed yet. Do next task
            ImageDownloader.getInstance(progressUpdateListener).download(urls.get(nextIndex),
                    new ChainModeCallbackHandler(nextIndex));
        } else {
            // All tasks completed. List "results" now holds the result

            DemoActivity.isTasksInProgress = false;
        }
    }
}




Способ 3:

Выполняйте задачи параллельно.

Pros
Параллельное выполнение помогает иногда экономить время


Образец

import java.util.ArrayList;
import java.util.List;

public class ParallelModeCallbackHandler {

    // List of args to start the task. Use pojo classes if your task has multiple args
    private static List<String> urls;

    // List of results
    private static List<String> results;

    // Leave it as it is
    private static int count;

    public static void start(List<String> urls, ImageDownloader.ProgressUpdateListener listener) {
        ParallelModeCallbackHandler.urls = urls;
        results = new ArrayList<>();
        count = 0;

        // Start all tasks
        for (String url :
                urls) {
            //Replace with your task and its callback
            ImageDownloader.getInstance(listener).download(url, new ImageDownloader.ImageDownloadCallback() {
                @Override
                public void onSuccess(String imagePath) {
                    results.add(imagePath);
                    afterCallback();
                }

                @Override
                public void onFailure() {
                    afterCallback();
                }
            });
        }
    }

    private ParallelModeCallbackHandler() {}

    private static void afterCallback() {
        if (++count==urls.size()) {
            // All tasks completed. List "results" now holds the result

            DemoActivity.isTasksInProgress = false;
        }
    }
}

Этот ТАК вопрос может быть хорошим началом.

Идея проста: дать кучу задач исполнителю, который позаботится о том, чтобы запустить их параллельно (даже если есть несколько возвращаемых объектов, если требуется), и дождаться завершения задач.

Используйте CountDownLatch, я скопирую здесь их пример использования для лучшей подсветки синтаксиса (:

class Driver { // ...
  void main() throws InterruptedException {
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(N);

    for (int i = 0; i < N; ++i) // create and start threads
      new Thread(new Worker(startSignal, doneSignal)).start();

    doSomethingElse();            // don't let run yet
    startSignal.countDown();      // let all threads proceed
    doSomethingElse();
    doneSignal.await();           // wait for all to finish
  }
}

class Worker implements Runnable {
  private final CountDownLatch startSignal;
  private final CountDownLatch doneSignal;
  Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
    this.startSignal = startSignal;
    this.doneSignal = doneSignal;
  }
  public void run() {
    try {
      startSignal.await();
      doWork();
      doneSignal.countDown();
    } catch (InterruptedException ex) {} // return;
  }

  void doWork() { ... }
}
Другие вопросы по тегам