Android 消息队列PriorityBlockingQueue(阻塞优先级队列)
、Comparable
首先看看要封装的队列需要有什么功能
在实际中,我们执行的任务大概可以分两种,一个是有
明确的执行时间的
,比如,要连续显示10个动画,每个展示5秒这种。一个是没明确的执行时间的
,比如连续上传10张图片,每个上传任务的完成时间是需要等到上传成功回调回来才知道的这种。所以队列第一个功能是每个任务都可以兼容这两种情况,而且当然是一个执行完再执行下一个,排队执行既然要排队执行,当然会有优先级之分,所以每个任务都能设置优先级,队列可以根据优先级去排队执行任务
1. 定义一个枚举类TaskPriority,定义任务的优先级
// 优先级分为3种,如注释所示,他们的关系:LOW < DEFAULT < HIGH
public enum TaskPriority {
LOW, //低
DEFAULT,//普通
HIGH, //高
}
2. 队列任务执行时间确定和不确定两种情况的实现策略
针对任务执行时间确定的这种情况,比较简单,可以给这个任务设置一个时间 duration,等任务开始执行时,便开始阻塞等待,等到时间到达时,才放开执行下一个任务
针对任务执行时间不确定的情况,我们则需要在任务执行完成的回调里面手动放开,所以这里打算再用一个PriorityBlockingQueue队列,因为它有这样的一个特点:如果队列是空的,调用它的 take()方法时,它就会一直阻塞在那里,当列表不为空时,这个方法就不会阻塞。 所以当任务开始执行时,调用take()方法,等到我们的完成回调来时,再手动给它add一个值,阻塞就放开,再执行下一个任务
一些解释:上面总共两个优先级阻塞队列,一个是用来执行任务的,是BlockTaskQueue 类中mTaskQueue ,另一个是mTaskQueue执行任务时,用来实现阻塞的,是定义在BaseTask 类中的blockQueue
3. 确定了任务两种情况的实现策略后,接下来定义一个接口,定义一下每个任务需要做什么事情
public interface ITask extends Comparable<ITask> {
// 将该任务插入队列
void enqueue();
// 执行具体任务的方法
void doTask();
// 任务执行完成后的回调方法
void finishTask();
// 设置任务优先级
ITask setPriority(TaskPriority mTaskPriority);
// 获取任务优先级
TaskPriority getPriority();
// 当优先级相同 按照插入顺序 先入先出 该方法用来标记插入顺序
void setSequence(int mSequence);
// 获取入队次序
int getSequence();
// 每个任务的状态,就是标记完成和未完成
boolean getStatus();
// 设置每个任务的执行时间,该方法用于任务执行时间确定的情况
ITask setDuration(int duration);
// 获取每个任务执行的时间
int getDuration();
// 阻塞任务执行,该方法用于任务执行时间不确定的情况
void blockTask() throws Exception;
// 解除阻塞任务,该方法用于任务执行时间不确定的情况
void unLockBlock();
}
4. 封装一下PriorityBlockingQueue的基本功能
public class BlockTaskQueue {
private String TAG = "BlockTaskQueue";
private AtomicInteger mAtomicInteger = new AtomicInteger();
//阻塞队列
private final BlockingQueue<ITask> mTaskQueue = new PriorityBlockingQueue<>();
private BlockTaskQueue() {
}
//单例模式
private static class BlockTaskQueueHolder {
private final static BlockTaskQueue INSTANCE = new BlockTaskQueue();
}
public static BlockTaskQueue getInstance() {
return BlockTaskQueueHolder.INSTANCE;
}
/**
* 插入时 因为每一个Task都实现了comparable接口 所以队列会按照Task复写的compare()方法定义的优先级次序进行插入
* 当优先级相同时,使用AtomicInteger原子类自增 来为每一个task 设置sequence,
* sequence的作用是标记两个相同优先级的任务入队的次序
*/
public <T extends ITask> int add(T task) {
if (!mTaskQueue.contains(task)) {
task.setSequence(mAtomicInteger.incrementAndGet());
mTaskQueue.add(task);
Log.d(TAG, "\n add task " + task.toString());
}
return mTaskQueue.size();
}
public <T extends ITask> void remove(T task) {
if (mTaskQueue.contains(task)) {
Log.d(TAG, "\n" + "task has been finished. remove it from task queue");
mTaskQueue.remove(task);
}
if (mTaskQueue.size() == 0) {
mAtomicInteger.set(0);
}
}
public ITask poll() {
return mTaskQueue.poll();
}
public ITask take() throws InterruptedException {
return mTaskQueue.take();
}
public void clear() {
mTaskQueue.clear();
}
public int size() {
return mTaskQueue.size();
}
}
5. 写一个类记录下当前执行的任务信息,方便获取时使用
public class CurrentRunningTask {
private static ITask sCurrentShowingTask;
public static void setCurrentShowingTask(ITask task) {
sCurrentShowingTask = task;
}
public static void removeCurrentShowingTask() {
sCurrentShowingTask = null;
}
public static ITask getCurrentShowingTask() {
return sCurrentShowingTask;
}
public static boolean getCurrentShowingStatus() {
return sCurrentShowingTask != null && sCurrentShowingTask.getStatus();
}
}
6. 封装一个基础的任务类
public class BaseTask implements ITask {
private final String TAG = getClass().getSimpleName();
private TaskPriority mTaskPriority = TaskPriority.DEFAULT; //默认优先级
private int mSequence;// 入队次序
private Boolean mTaskStatus = false; // 标志任务状态,是否仍在展示
protected WeakReference<BlockTaskQueue> taskQueue;//阻塞队列
protected int duration = 0; //任务执行时间
//此队列用来实现任务时间不确定的队列阻塞功能
private PriorityBlockingQueue<Integer> blockQueue;
//构造函数
public BaseTask() {
taskQueue = new WeakReference<>(BlockTaskQueue.getInstance());
blockQueue = new PriorityBlockingQueue<>();
}
//入队实现
@Override
public void enqueue() {
TaskScheduler.getInstance().enqueue(this);
}
//执行任务方法,此时标记为设为true,并且将当前任务记录下来
@Override
public void doTask() {
mTaskStatus = true;
CurrentRunningTask.setCurrentShowingTask(this);
}
//任务执行完成,改变标记位,将任务在队列中移除,并且把记录清除
@Override
public void finishTask() {
this.mTaskStatus = false;
this.taskQueue.get().remove(this);
CurrentRunningTask.removeCurrentShowingTask();
Log.d(TAG, taskQueue.get().size() + "");
}
//设置任务优先级实现
@Override
public ITask setPriority(TaskPriority mTaskPriority) {
this.mTaskPriority = mTaskPriority;
return this;
}
//设置任务执行时间
public ITask setDuration(int duration) {
this.duration = duration;
return this;
}
//获取任务优先级
@Override
public TaskPriority getPriority() {
return mTaskPriority;
}
//获取任务执行时间
@Override
public int getDuration() {
return duration;
}
//设置任务次序
@Override
public void setSequence(int mSequence) {
this.mSequence = mSequence;
}
//获取任务次序
@Override
public int getSequence() {
return mSequence;
}
// 获取任务状态
@Override
public boolean getStatus() {
return mTaskStatus;
}
//阻塞任务执行
@Override
public void blockTask() throws Exception {
blockQueue.take(); //如果队列里面没数据,就会一直阻塞
}
//解除阻塞
@Override
public void unLockBlock() {
blockQueue.add(1); //往里面随便添加一个数据,阻塞就会解除
}
/**
* 排队实现
* 优先级的标准如下:
* TaskPriority.LOW < TaskPriority.DEFAULT < TaskPriority.HIGH
* 当优先级相同 按照插入次序排队
*/
@Override
public int compareTo(ITask another) {
final TaskPriority me = this.getPriority();
final TaskPriority it = another.getPriority();
return me == it ? this.getSequence() - another.getSequence() :
it.ordinal() - me.ordinal();
}
//输出一些信息
@Override
public String toString() {
return "task name : " + getClass().getSimpleName() + " sequence : " + mSequence + " TaskPriority : " + mTaskPriority;
}
}
7. TaskScheduler
public class TaskScheduler {
private final String TAG = "TaskScheduler";
private BlockTaskQueue mTaskQueue = BlockTaskQueue.getInstance();
private ShowTaskExecutor mExecutor;
private static class ShowDurationHolder {
private final static TaskScheduler INSTANCE = new TaskScheduler();
}
private TaskScheduler() {
initExecutor();
}
private void initExecutor() {
mExecutor = new ShowTaskExecutor(mTaskQueue);
mExecutor.start();
}
public static TaskScheduler getInstance() {
return ShowDurationHolder.INSTANCE;
}
public void enqueue(ITask task) {
//因为TaskScheduler这里写成单例,如果isRunning改成false的话,不判断一下,就会一直都是false
if (!mExecutor.isRunning()) {
mExecutor.startRunning();
}
//按照优先级插入队列 依次播放
mTaskQueue.add(task);
}
public void resetExecutor() {
mExecutor.resetExecutor();
}
public void clearExecutor() {
mExecutor.clearExecutor();
}
}
8. 管理任务队列:ShowTaskExecutor
public class ShowTaskExecutor {
private final String TAG = "ShowTaskExecutor";
private BlockTaskQueue taskQueue;
private TaskHandler mTaskHandler;
private boolean isRunning = true;
private static final int MSG_EVENT_DO = 0;
private static final int MSG_EVENT_FINISH = 1;
public ShowTaskExecutor(BlockTaskQueue taskQueue) {
this.taskQueue = taskQueue;
mTaskHandler = new TaskHandler();
}
//开始遍历任务队列
public void start() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
try {
while (isRunning) { //死循环
ITask iTask;
iTask = taskQueue.take(); //取任务
if (iTask != null) {
//执行任务
TaskEvent doEvent = new TaskEvent();
doEvent.setTask(iTask);
doEvent.setEventType(TaskEvent.EventType.DO);
mTaskHandler.obtainMessage(MSG_EVENT_DO, doEvent).sendToTarget();
//一直阻塞,直到任务执行完
if (iTask.getDuration()!=0) {
TimeUnit.MILLISECONDS.sleep(iTask.getDuration());
}else {
iTask.blockTask();
}
//完成任务
TaskEvent finishEvent = new TaskEvent();
finishEvent.setTask(iTask);
finishEvent.setEventType(TaskEvent.EventType.FINISH);
mTaskHandler.obtainMessage(MSG_EVENT_FINISH, finishEvent).sendToTarget();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
}
//根据不同的消息回调不同的方法。
private static class TaskHandler extends Handler {
TaskHandler() {
super(Looper.getMainLooper());
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
TaskEvent taskEvent = (TaskEvent) msg.obj;
if (msg.what == MSG_EVENT_DO && taskEvent.getEventType() == TaskEvent.EventType.DO) {
taskEvent.getTask().doTask();
}
if (msg.what == MSG_EVENT_FINISH && taskEvent.getEventType() == TaskEvent.EventType.FINISH) {
taskEvent.getTask().finishTask();
}
}
}
public void startRunning() {
isRunning = true;
}
public void pauseRunning() {
isRunning = false;
}
public boolean isRunning() {
return isRunning;
}
public void resetExecutor() {
isRunning = true;
taskQueue.clear();
}
public void clearExecutor() {
pauseRunning();
taskQueue.clear();
}
}
9. TaskEvent
public class TaskEvent {
private WeakReference<ITask> mTask;
int mEventType;
public ITask getTask() {
return mTask.get();
}
public void setTask(ITask mTask) {
this.mTask = new WeakReference<>(mTask);
}
public int getEventType() {
return mEventType;
}
public void setEventType(int mEventType) {
this.mEventType = mEventType;
}
public static class EventType {
public static final int DO = 0X00;
public static final int FINISH = 0X01;
}
}
10.使用方法
定义一个Task,继承 BaseTask,并实现对应的方法
public class LogTask extends BaseTask {
String name;
public LogTask(String name) {
this.name = name;
}
//执行任务方法,在这里实现你的任务具体内容
@Override
public void doTask() {
super.doTask();
Log.i("LogTask", "--doTask-" + name);
//如果这个Task的执行时间是不确定的,比如上传图片,那么在上传成功后需要手动调用
//unLockBlock方法解除阻塞,例如:
uploadImage(new UploadListener{
public void onSuccess() {
unLockBlock();
}
});
}
private void uploadImage(UploadListener uploadListener) {
try {
// long seconds = getDuration();
long seconds = (long) (Math.random() * 10000);
Log.i("LogTask", "uploadImage milliseconds " + seconds);
Thread.sleep(seconds);
} catch (Exception e) {
e.printStackTrace();
}
uploadListener.onSuccess();
}
//任务执行完的回调,在这里你可以做些释放资源或者埋点之类的操作
@Override
public void finishTask() {
super.finishTask();
Log.i("LogTask", "--finishTask-" + name);
}
}
public interface UploadListener {
void onSuccess();
}
然后依次入队使用
findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new LogTask("任务2")
// .setDuration(4000) // 设置了时间,代表这个任务时间是确定的,如果不确定,则不用设置
.setPriority(TaskPriority.DEFAULT) // 设置优先级,默认是DEFAULT
.enqueue(); // 入队
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
new LogTask("任务4")
// .setDuration(4000) // 设置了时间,代表这个任务时间是确定的,如果不确定,则不用设置
.setPriority(TaskPriority.LOW) // 设置优先级,默认是DEFAULT
.enqueue(); // 入队
new LogTask("任务1")
// .setDuration(6000) // 设置了时间,代表这个任务时间是确定的,如果不确定,则不用设置
.setPriority(TaskPriority.LOW) // 设置优先级,默认是DEFAULT
.enqueue(); // 入队
new LogTask("任务3")
// .setDuration(2000) // 设置了时间,代表这个任务时间是确定的,如果不确定,则不用设置
.setPriority(TaskPriority.HIGH) // 设置优先级,默认是DEFAULT
.enqueue(); // 入队
}
},1000);
}
});