本文为文章《handler思想的个人简单实现》的续作。
https://blog.csdn.net/cjzjolly/article/details/79386416
在平常开发过程中,常常会用到handler里面的postDelay作为一种执行延时任务的工具,其实就是UI线程循环里面塞了点“插曲”,比Timer节约一点资源。我突发奇想想试试在不看它代码实现的情况下能不能实现出类似的Handler,首先我想到了加入一个Runnable之后直接上sleep,可是这样太耗费资源了,所以我最后使用了一个后台线程执行倒计时,时间到了或者超过了任务的预定时刻,就执行handler任务列表里面相应的需要该时刻执行的runnable。这样做的好处是占用资源少,但实际上这个方法在任务表的runnable变多的时候,其实是时间变得不那么精确的。
代码不是十分长,也比较容易看懂,看看注释一定能懂,所以直接贴代码啦。以下就是我的实现方法:
Handler类:
package com.test.handler;
import java.util.ArrayList;
import java.util.List;
public class Handler {
private List<Task> taskList = new ArrayList<>();
/** 任务单元 **/
private class Task {
/** 任务runnalbe **/
public Runnable runnable;
/** 延迟多少毫秒之后执行Runnable **/
public long delayed;
public Task(Runnable runnable, long delayed) {
this.runnable = runnable;
this.delayed = delayed;
}
}
private Object endLock = new Object();
/** 内部循环 **/
private class LoopThread extends Thread {
long distance = 0;
@Override
public void run() {
super.run();
while (!taskList.isEmpty()) {
long start = System.currentTimeMillis();
for (int i = 0; i < taskList.size(); i++) {
Task task = taskList.get(i);
synchronized (task) {
if (task != null) {
if ((task.delayed -= distance < 1 ? 1 : distance) <= 0) {
new Thread(task.runnable).start();
taskList.remove(i);
i--;
}
}
}
}
long end = System.currentTimeMillis();
distance = end - start;
try {
Thread.sleep(distance < 1 ? 1 : 0);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println("已路过时间:" + distance);
}
synchronized (endLock) {
// 线程已经完成所有任务,清空,退出,不占额外资源
loopThread = null;
}
}
}
private LoopThread loopThread = null;
/**添加延时任务**/
public void postDealyed(Runnable r, long dealyMS) {
taskList.add(new Task(r, dealyMS));
synchronized (endLock) {
if (loopThread == null) { // 如果线程为空
loopThread = new LoopThread();
loopThread.start();
}
}
}
/**删除延时任务**/
public void remove(Runnable r){
for (int i = 0; i < taskList.size(); i++) {
Task task = taskList.get(i);
synchronized (task) {
if (task != null && task.runnable == r) {
taskList.remove(i);
break;
}
}
}
}
}
测试类:
package com.test.handler;
public class TestMain {
public static void main(String args[]){
Handler handler = new Handler();
//测试同时加几个延时任务
handler.postDealyed(new Runnable() {
@Override
public void run() {
System.out.println("HandlerTest1");
}
}, 3000);
handler.postDealyed(new Runnable() {
@Override
public void run() {
System.out.println("HandlerTest2");
}
}, 5000);
handler.postDealyed(new Runnable() {
@Override
public void run() {
System.out.println("HandlerTest3");
}
}, 0);
//测试移除:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("HandlerTest4");
}
};
handler.postDealyed(r, 6000);
handler.remove(r);
//测试在Handler内部循环线程已经结束的时候再加一个任务
try {
Thread.sleep(9000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.postDealyed(new Runnable() {
@Override
public void run() {
System.out.println("end");
}
}, 300);
}
}
测试效果:
//20190331 修正了一些效率问题,采用ArrayList代替LinkedList提高遍历速度