前言
Timer是个轻量级的定时控件,一般项目中不太常见,对于普通业务一般可以用handler或者alarmmaager来实现,但是最近研究thread。就看了下Timer类。忽然发现设计也是很优秀的。这里就对这个控件进行了分析。
用法
Timer主要是针对定时刷新等业务的一种简单实现,一般代码如下:
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
Log.v(">>>>>", "it happen");
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
time = System.currentTimeMillis();
Timer timer = new Timer(true);
timer.schedule(timerTask, 10000, 10000); //延时1000ms后执行,1000ms执行一次
//timer.cancel(); 这是取消定时器的指令
使用方法非常简单。系统仅仅提供了三个基本控制函数
1. schedule()这是设置task的函数这里有五个变种,就不在细说。一会源码分析会有。
2. cancel()是取消所有的task,注意这里不会影响当前正在运行的task。
3. purge()移除所有任务。
这里功能基本费心完成。我们来研究源码
源码分析
在研究源码时候,基本都是从构造函数入手。这里我们来看看构造函数。
public Timer() { this(false); } //调用了自身的构造函数
public Timer(boolean isDaemon) {
this("Timer-" + Timer.nextId(), isDaemon); //再次调用自己构造函数
}
//这是最重要的一个构造函数。里面name线程名字。isDaemon是代表timer所在的线程是否是守护进程。前两个默认的的是非守护进程。getname()函数是获取的是一个唯一的序列号。
public Timer(String name, boolean isDaemon) {
if (name == null) {
throw new NullPointerException("name == null");
}
this.impl = new TimerImpl(name, isDaemon);
this.finalizer = new FinalizerHelper(impl);
}
这里我们立马就会发现两个全局变量imp和finalizer他们是有干什么的那么我们就开始看他的源码。这里首先看个简单的,一个静态内部类。
private static final class FinalizerHelper {
private final TimerImpl impl;
FinalizerHelper(TimerImpl impl) {
this.impl = impl;
}
@Override protected void finalize() throws Throwable {
try {
synchronized (impl) {
impl.finished = true;
impl.notify();
}
} finally {
super.finalize();
}
}
}
这个类很有意思。他仅仅是在自己被垃圾回收机制给回收前给imp的finshed给设成true,(这里我有个疑问为啥不直接在timer类中复写finalize()方法,ps有个大神给我说是面向对象的单一职责原则,貌似一定意义上说的通)。
实例化完成后我们本来应该看TimerImpl类的,但是有点复杂,我们就跟踪这个timer流程来走schedule()函数。这里所有的schedule()函数都指向这一个函数。下面我们来分析下。
private void scheduleImpl(TimerTask task, long delay, long period, boolean fixed) {
synchronized (impl) {
if (impl.cancelled) {
throw new IllegalStateException("Timer was canceled");
}
long when = delay + System.currentTimeMillis();
if (when < 0) {
throw new IllegalArgumentException("Illegal delay to start the TimerTask: " + when);
}
synchronized (task.lock) { //这里检车是否已经存在任务中了。不还重点。总之这一切都是为了安全
if (task.isScheduled()) {
throw new IllegalStateException("TimerTask is scheduled already");
}
if (task.cancelled) {
throw new IllegalStateException("TimerTask is canceled");
}
task.when = when;
task.period = period;
task.fixedRate = fixed;
}
// insert the newTask into queue
//这里是重点。所有的任务表明这里仅仅是让给imp来处理。imp是implement的简写,是实现类
impl.insertTask(task);
}
}
这里我们先放下impl.insertTask(task);。开始研究task
public abstract class TimerTask implements Runnable {
final Object lock = new Object();
/* If timer was cancelled */
boolean cancelled;
//什么时候开启
long when;
long period;
boolean fixedRate;
......
public abstract void run();
这里几个函数主要是主要是为了获取参数和异步访问安全的问题。主要知道这几个参数,最重要是实现这个run()方法,主要的事件是在这里处理的。这里了解了TimerTask 。那么就开始这个最难啃的TimerImpl这个timer的具体实现类。
首先构造函数
TimerImpl(String name, boolean isDaemon) {
this.setName(name); //使用线程必须有name
this.setDaemon(isDaemon); //必须设置。
this.start(); 开始了当前的线程。
}
这里的TimerImpl是一个thread。真正的方法都在threadimp中运行,这也就是我们知道的为啥不能在timer中改变ui的原因了。接下类我们看run()方法:
@Override
public void run() {
while (true) {
TimerTask task;
synchronized (this) {
// need to check cancelled inside the synchronized block
if (cancelled) {
return;
}
if (tasks.isEmpty()) {
if (finished) {
return;
}
// no tasks scheduled -- sleep until any task appear
//没有消息就等待
try {
this.wait();
} catch (InterruptedException ignored) {
}
continue;
}
long currentTime = System.currentTimeMillis();
task = tasks.minimum();
long timeToSleep;
//消息取消了就直接删除
synchronized (task.lock) {
if (task.cancelled) {
tasks.delete(0);
continue;
}
// check the time to sleep for the first task scheduled
timeToSleep = task.when - currentTime;
}
if (timeToSleep > 0) {
// sleep!
try {
this.wait(timeToSleep);
} catch (InterruptedException ignored) {
}
continue;
}
// no sleep is necessary before launching the task
//到这里我也看得不太懂前半分是为了验证task的正确性。后半分是在添加吓一跳task
synchronized (task.lock) {
int pos = 0;
//这里感觉不太会自己when和自己不相等。感觉是为了安全监测
if (tasks.minimum().when != task.when) {
pos = tasks.getTask(task);
}
//又一次监测
if (task.cancelled) {
tasks.delete(tasks.getTask(task));
continue;
}
// set time to schedul
task.setScheduledTime(task.when);
// remove task from queue
//删除当前的task、
tasks.delete(pos);
// set when the next task should be launched
//貌似是为了设置下个一个task
if (task.period >= 0) {
// this is a repeating task,
if (task.fixedRate) {
// task is scheduled at fixed rate
task.when = task.when + task.period;
} else {
// task is scheduled at fixed delay
task.when = System.currentTimeMillis()
+ task.period;
}
// insert this task into queue
insertTask(task);
} else {
task.when = 0;
}
}
}
boolean taskCompletedNormally = false;
try {
//在这里执行代码。这里我们仅仅是调用了run方法。还是在timerImp的thread中执行
task.run();
taskCompletedNormally = true;
} finally {
if (!taskCompletedNormally) {
synchronized (this) {
cancelled = true;
}
}
}
}
}
这里是run()方法,里面我只要知道timerImp中有一个用来保存task的全局变量tasks,这个类可以自动对task进行对时间的排序。和添加任务。这里我们从开始往下看主意英文注释。前面很好理解。到了后面我给出了注释,在task.run()之前都是安全检测与等待,这里就是主要的内容。在这里我们看到了梦想中task的内容被调用,这里我在分析下消息对列tasks的添加吧。调用是在之前看到的
java impl.insertTask(task);
这里找到方法是
private void insertTask(TimerTask newTask) {
// callers are synchronized
//这是关键。
tasks.insert(newTask);
//这里是让线程在sleep中唤醒
this.notify();
}
我们找打tasks的TimerHeap类中insert方法
public void insert(TimerTask task) {
//开始不会进入因为是为了timers的长度达到最大值时的处理方法
if (timers.length == size) {
TimerTask[] appendedTimers = new TimerTask[size * 2];
system.arraycopy(timers, 0, appendedTimers, 0, size);
timers = appendedTimers;
}
//这里就是添加task
timers[size++] = task;
//排序,这里我就不在写了。有兴趣看以看看。
upHeap();
}
至此timer类完成。仅仅500多行的代码写了这么多。但是里面确实是一个小型的消息处理机制。很有学习价值。