Многократный обратный вызов в 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() { ... }
}