在app开发中经常会有这样一种需求:在多个线程中并发执行多个任务,当且仅当这些任务全部完成时,才继续进行下一步操作。以app的欢迎页为例,通常需要同时做这样几件事:1 获取基础的配置信息 2 检查更新 3 计时n秒(这是为了保证欢迎页至少被展示n秒),当且仅当这3个任务均被完成时,才跳转到app的主界面。
通常的做法是写几个状态变量与并发执行的任务一一对应,每当一个任务完成时,就去更新并检查这些变量——这种做法略显繁琐;另一种做法是使用Rxjava操作符,但在多人维护的老项目中,如果有成员对Rxjava不太熟悉,那么贸然引入Rxjava显然也不是一个好选择;而第三种选择就是使用ConcurrentTasksManager这个简单的工具类。
ConcurrentTasksManager中有两个内部类,即执行器Executer和计数器Counter,它们各自有不同的应用场景。
Executer
Executer用来并发执行多个同步任务(对于okhttp和retrofit,同步意味着调用call.execute(...)
而不是call.enque(...)
),当所有任务均完成时,会通过回调通知用户。
以上面提到的欢迎页为例,先创建3个同步任务:
//(同步方式)计时3秒
Runnable timeout = new Runnable() {
@Override
public void run() {
SystemClock.sleep(3000);
}
};
//(同步方式)获取配置参数
Runnable getConfig = new Runnable() {
@Override
public void run() {
Call<Config> call = ApiManager.getApiService().getConfig();
Response<Config> configResponse = call.execute();
......
}
};
//(同步方式)检查是否有更新
Runnable checkUpdate = new Runnable() {
@Override
public void run() {
Call<UpdateInfo> call = ApiManager.getApiService().getUpdateInfo();
Response<UpdateInfo> updateInfoResponse = call.execute();
......
}
};
将创建好的同步任务交给Executer去执行——通过addTask(...)
来添加任务,通过start(...)
来并发执行添加的多个任务。当所有任务均完成时,onAllFinished方法会被调用:
ConcurrentTasksManager.newExecuter()//创建Executer。newExecuter(...)方法可接收一个线程池参数
.addTask(timeout)
.addTask(getConfig)
.addTask(checkUpdate)
.start(new ConcurrentTasksManager.Executer.OnAllFinishedListener() {
@Override
public void onAllFinished() {
//全部任务均已完成,可以进行下一步操作了
}
});
Counter
如果有几个已经写好的异步任务(对于okhttp和retrofit,异步意味着调用call.enque(...)
而不是call.execute(...)
),又不想去将它们改为同步任务,那么可以使用计数器Counter来帮助简化代码。
首先是告诉Counter总共有多少个异步任务:
ConcurrentTasksManager.Counter counter = ConcurrentTasksManager.newCounter(3);//有3个异步任务需要计数
然后开始各个异步任务的执行,并在每个异步任务执行完毕时调用notifyAndCheckIfAllFinished()方法来通知counter当前任务已完成并检查是否全部的任务均已完成:
Call<Config> call = ApiManager.getApiService().getConfig();
call.enqueue(new Callback<Config>() {
@Override
public void onResponse(Call<Config> call, Response<Config> response) {
......
if (counter.notifyAndCheckIfAllFinished()) {
//全部任务均已完成,可以进行下一步操作了
}
}
@Override
public void onFailure(Call<Config> call, Throwable t) {
......
if (counter.notifyAndCheckIfAllFinished()) {
//全部任务均已完成,可以进行下一步操作了
}
}
});
工具类代码
代码未完善与优化,仅供参考。
import java.util.ArrayList;
import java.util.concurrent.ThreadPoolExecutor;
public class ConcurrentTasksManager {
public static Executer newExecuter() {
return new Executer(null);
}
public static Executer newExecuter(ThreadPoolExecutor threadPoolExecutor) {
return new Executer(threadPoolExecutor);
}
public static Counter newCounter(int tasksCount) {
return new Counter(tasksCount);
}
//------------------------------------------------------------------------------------------
/**
* 执行器(并发执行多个同步任务,当所有任务均完成时,通过回调通知用户)
*/
public static class Executer {
private ThreadPoolExecutor threadPoolExecutor;
private ArrayList<Runnable> tasks = new ArrayList<>();
private int finishedCount;//已完成的任务数
private int tasksCount;//总任务数
private OnAllFinishedListener listener;
public interface OnAllFinishedListener {
void onAllFinished();
}
public Executer(ThreadPoolExecutor threadPoolExecutor) {
this.threadPoolExecutor = threadPoolExecutor;
}
public Executer addTask(Runnable task) {
if (task != null) {
tasks.add(task);
}
return this;
}
public void start(OnAllFinishedListener listener) {
if (tasks.size() == 0) return;
this.listener = listener;
finishedCount = 0;
tasksCount = tasks.size();
for (final Runnable task : tasks) {
//包装任务
Runnable runnable = new Runnable() {
@Override
public void run() {
task.run();
increaseAndCheck();
}
};
if (threadPoolExecutor != null) {//传入了线程池,使用线程池执行
threadPoolExecutor.execute(runnable);
} else {//未传入线程池,则手动开启线程执行
new Thread(runnable).start();
}
}
}
/**
* 检查是否全部任务已完成
*/
private synchronized void increaseAndCheck() {
finishedCount++;
if (finishedCount == tasksCount && listener != null) {//任务已全部完成
listener.onAllFinished();
}
}
}
/**
* 计数器(管理多个异步任务,当所有异步任务都完成时,notifyAndCheck方法会返回true)
*/
public static class Counter {
private int finishedCount;//已完成的任务数
private int tasksCount;//总任务数
/**
* @param tasksCount:总的任务数
*/
public Counter(int tasksCount) {
this.tasksCount = tasksCount;
}
/**
* 通知counter当前任务已完成,并检查是否全部任务都已完成
*
* @return true:全部任务已完成
*/
public synchronized boolean notifyAndCheckIfAllFinished() {
finishedCount++;
return (finishedCount == tasksCount);
}
}
}