【Android 异步操作】Timer 定时器 ( Timer 与 TimerTask 基本使用 | Timer 定时器常用用法 | Timer 源码分析 )
文章目录
- 一、Timer 定时器基本使用
- 二、Timer 定时器常用用法
- 三、Timer 源码分析
- 四、Timer 部分源码注释
- 五、源码及资源下载
参考文档 :
- Timer 定时器 API 文档
- TimerTask 定时器任务 API 文档
一、Timer 定时器基本使用
Timer 可用于执行延迟任务或循环任务 ; 下面是定时器最基本用法 ;
1 . Timer 定时器基本使用 :
- 创建 Timer 定时器 : 调用构造函数创建定时器 Timer timer = new Timer() ;
- 分配 TimerTask 定时器任务 : 调用定时器的 schedule 方法 , 为 Timer 定时器分配 TimerTask 定时器任务 ;
2 . 定时器任务执行规则 : Timer 执行任务是 串行执行 的 , 同一时间只能执行一个任务 ;
在下面的示例中
- 任务 1 在 1 秒之后执行 , 在第 6 秒执行完毕 ;
- 任务 2 在第 6 秒 , 任务 1 执行完毕后 , 才开始执行 , 在第 11 秒执行完毕 ;
3 . 代码示例 :
private void timer(){// Timer 可用于执行延迟任务或循环任务Timer timer = new Timer();/*如果提交多个 TimerTask 定时器任务需要等待之前的 定时器任务 执行完成 , 才能执行后面的任务TimerTask 实现了 Runnable 接口延迟 1 秒执行任务 1 ( 任务 1 时长 5 秒 )延迟 2 秒执行任务 2 ( 任务 2 时长 5 秒 )Timer 执行任务是串行执行的 , 同一时间只能执行一个任务任务 1 在 1 秒之后执行 , 在第 6 秒执行完毕任务 2 在第 6 秒 , 任务 1 执行完毕后 , 才开始执行 , 在第 11 秒执行完毕*/// 延迟 1 秒执行任务 1timer.schedule(new TimerTask() {@Overridepublic void run() {Log.i(TAG, "延迟 1 秒执行 5 秒的任务 1 开始执行");try {Thread.sleep(5_000);} catch (InterruptedException e) {e.printStackTrace();}Log.i(TAG, "延迟 1 秒执行 5 秒的任务 1 执行完毕");}}, 1_000);// 延迟 2 秒执行任务 2timer.schedule(new TimerTask() {@Overridepublic void run() {Log.i(TAG, "延迟 2 秒执行 5 秒的任务 2 开始执行");try {Thread.sleep(5_000);} catch (InterruptedException e) {e.printStackTrace();}Log.i(TAG, "延迟 2 秒执行 5 秒的任务 2执行完毕");}}, 2_000);}二、Timer 定时器常用用法
1 . Timer 定时器构造函数 :
① 创建默认定时器 : 默认以 “Timer-序列号” 作为定时器线程名称 ;
public Timer() { this("Timer-" + serialNumber()); }② 创建守护线程定时器 : 指定定时器是否作为守护线程来执行 , 定时器线程名称是默认名称 ;
public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); }③ 创建定时器并指定定时器名称 : 创建定时器 , 并 设置定时器线程名称 ;
public Timer(String name) {thread.setName(name);thread.start();}④ 指定名称并设置守护线程 : 同时 设置定时器名称 , 并 设置定时器是否是守护线程 ;
public Timer(String name, boolean isDaemon) {thread.setName(name);thread.setDaemon(isDaemon);thread.start();}2 . 定时器调度方法 :
① 在指定一段时间后执行定时器任务 : 在 delay 毫秒后 , 执行 TimerTask 定时器任务 ;
public void schedule(TimerTask task, long delay) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");sched(task, System.currentTimeMillis()+delay, 0);}② 在指定的时间执行定时器任务 : 在某个指定的时间执行 TimerTask 定时器任务 ;
public void schedule(TimerTask task, Date time) { sched(task, time.getTime(), 0); }③ 在指定的时间执行循环任务 : 在 firstTime 时间执行第一次 TimerTask 定时器任务 , 之后每隔 period 毫秒的周期时间 , 循环执行定时器任务 ; 循环周期是 period 毫秒 ; 如果因为某种原因导致某些操作出现了延迟 , 那么后续操作也会跟着延迟 ;
public void schedule(TimerTask task, Date firstTime, long period) {if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, firstTime.getTime(), -period);}④ 延迟指定时间循环执行任务 : 延迟 delay 毫秒后 , 执行第一次定时器任务 , 然后每隔 period 毫秒 , 循环执行定时器任务 ; 循环周期是 period 毫秒 ; 如果因为某种原因导致某些操作出现了延迟 , 那么后续操作也会跟着延迟 ;
public void schedule(TimerTask task, long delay, long period) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, System.currentTimeMillis()+delay, -period);}⑤ 在指定的时间执行循环任务 : 在 firstTime 时间执行第一次 TimerTask 定时器任务 , 之后每隔 period 毫秒的周期时间 , 循环执行定时器任务 ; 循环周期是 period 毫秒 ; 如果因为某种原因导致某些操作出现了延迟 , 那么后续操作需要补偿上述出现的延迟 ;
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, System.currentTimeMillis()+delay, period);}⑥ 延迟指定时间循环执行任务 : 延迟 delay 毫秒后 , 执行第一次定时器任务 , 然后每隔 period 毫秒 , 循环执行定时器任务 ; 循环周期是 period 毫秒 ; 如果因为某种原因导致某些操作出现了延迟 , 那么后续操作需要补偿上述出现的延迟 ;
public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period) {if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, firstTime.getTime(), period);}schedule 方法适用场景 : 适合用于平稳执行某种任务 ; 稳定性 > 准确率 ;
scheduleAtFixedRate 方法适用场景 : 适合用于对绝对时间敏感的任务 ; 准确率 > 稳定性 ;
三、Timer 源码分析
在 Timer 中定义了 TimerThread thread 成员变量 , 该成员对象在创建对象时会自动创建 ;
TimerThread 是定义在 Timer.java 文件中的类 , 是一个自定义线程类 ; 该帮助类实现了定时器任务的执行线程 , 该线程中的定时器队列等待任务到来 , 在合适的时间执行定时器任务 ; 调度需要重复执行的任务 ; 从任务队列中 , 移出被取消的任务 , 移出不需要循环执行的任务 ;
class TimerThread extends Thread{}在构造函数 public Timer(String name) 中 , 调用了该线程的 start() 方法 , 启动了该线程 ;
// 省略了无关代码 public class Timer {private final TimerThread thread = new TimerThread(queue);public Timer(String name) {// 启动 TimerThread 线程 thread.start();} }在 TimerThread 自定义线程中的 run() 方法中 , 主要是调用了 mainLoop() 方法 ; 该方法中是一个死循环 , 从循环队列中取出 TimerTask 定时器任务 , 然后执行 ; 必须等待前一个任务执行完毕 , 才能执行下一个任务 ;
四、Timer 部分源码注释
// 省略了无关代码 public class Timer {/*** 定时器线程 * 该 TimerThread thread 对象在创建对象时会自动创建 */// Android-added: @ReachabilitySensitive@ReachabilitySensitiveprivate final TimerThread thread = new TimerThread(queue);// 生成序列号 , 作为定时器线程的默认名称 private static int serialNumber() {return nextSerialNumber.getAndIncrement();}/*** 创建默认定时器 , TimerThread thread 成员变量不作为守护线程*/public Timer() {this("Timer-" + serialNumber());}/*** 创建一个定时器 , 其关联的 TimerThread thread 成员可以设置是否作为守护线程 ;* 如果该定时器用于用于调度重复性的维护活动 , 其守护线程会被调用 , * 应用运行期间必须调用守护线程 , * 但是上述操作不能影响应用的生命周期 ; ** @param isDaemon 如果设置成 true , TimerThread thread 需要被设置成守护线程 */public Timer(boolean isDaemon) {this("Timer-" + serialNumber(), isDaemon);}/*** 创建一个 Timer 定时器 , 并为其其关联的线程设置指定的名称 ; ** @param name 为 TimerThread 成员变量设置名称*/public Timer(String name) {thread.setName(name);// 启动 TimerThread 线程 thread.start();}/*** Creates a new timer whose associated thread has the specified name,* and may be specified to* {@linkplain Thread#setDaemon run as a daemon}.** @param name 设置关联的定时器线程名称* @param isDaemon 如果设置成 true 定时器线程将被设置成守护线程*/public Timer(String name, boolean isDaemon) {thread.setName(name);thread.setDaemon(isDaemon);thread.start();}}/*** TimerThread 是定义在 Timer.java 文件中的类 ; * * 该帮助类实现了定时器任务的执行线程 , 该线程中的定时器队列等待任务到来 , 在合适的时间执行定时器任务 ; * 调度需要重复执行的任务 ;* 从任务队列中 , 移出被取消的任务 , 移出不需要循环执行的任务 ; */ class TimerThread extends Thread {/*** 该标志为会 被 reaper 设置成 false , 用于通知我们定时器对象已经被销毁 ; * 一旦该标志位设置成 true , 并且任务队列中没有任务时 , 没有任务去执行 , 这里就需要优雅的关闭定时器 ; * 注意该字段由队列管理器维护 ; */boolean newTasksMayBeScheduled = true;/*** 任务队列 , 调用 schedule 方法调度 TimerTask 定时器任务 , 就是将任务加入到该队列中 ; * 定时器任务队列 , 在定时器线程中维护该队列 , 而不是在定时器中维护 , 这样避免循环引用 ; * 否则定时器永远不会被回收 , 并且该线程永远不会消失 ; */private TaskQueue queue;TimerThread(TaskQueue queue) {this.queue = queue;}// run 方法中主要是调用 mainLoop() 方法 ; public void run() {try {mainLoop();} finally {// 如果定时器被取消 , 需要杀死该线程synchronized(queue) {newTasksMayBeScheduled = false;queue.clear(); // 消除过时的引用}}}/*** 定时器轮询 * 里面是一个死循环 , 循环从人物队列中取出 TimerTask 定时器任务 , 然后执行 ; * 必须等待前一个任务执行完毕 , 才能执行下一个任务 */private void mainLoop() {while (true) {try {TimerTask task; // 定时器任务 boolean taskFired; synchronized(queue) {// 如果队列为空 , 一直阻塞 , 当有任务进来时 , 解除阻塞while (queue.isEmpty() && newTasksMayBeScheduled)queue.wait();if (queue.isEmpty())break; // 如果队列为空 , 就会退出循环 , 终止该定时器 // 队列不为空 , 查看第一个 evt , 并执行相应操作 long currentTime, executionTime;task = queue.getMin();synchronized(task.lock) {if (task.state == TimerTask.CANCELLED) {queue.removeMin();continue; // 任务被取消 , 从队列中移除}currentTime = System.currentTimeMillis();executionTime = task.nextExecutionTime;if (taskFired = (executionTime<=currentTime)) {if (task.period == 0) { // 该任务不需要循环执行 , 从队列中移除 queue.removeMin();task.state = TimerTask.EXECUTED;} else { // 该任务需要循环执行 , 继续调度queue.rescheduleMin(task.period<0 ? currentTime - task.period: executionTime + task.period);}}}if (!taskFired) // 等待前一个任务执行完毕 , 如果之前的任务没有执行完毕 , 一直阻塞queue.wait(executionTime - currentTime);}if (taskFired) // 任务执行完毕task.run();} catch(InterruptedException e) {}}} }
五、源码及资源下载
源码及资源下载地址 :
-
① GitHub 工程地址 : Android_Asynchronous
-
② MainActivity.java 主界面代码地址 : MainActivity.java , 这是上述示例代码位置 ; 仅做参考意义不大 ;
总结
以上是生活随笔为你收集整理的【Android 异步操作】Timer 定时器 ( Timer 与 TimerTask 基本使用 | Timer 定时器常用用法 | Timer 源码分析 )的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 【运筹学】线性规划 单纯形法 阶段总结
- 下一篇: 【Android 高性能音频】Oboe