【Android 异步操作】Timer 定时器 ( Timer 与 TimerTask 基本使用 | Timer 定时器常用用法 | Timer 源码分析 )
文章目錄
- 一、Timer 定時(shí)器基本使用
- 二、Timer 定時(shí)器常用用法
- 三、Timer 源碼分析
- 四、Timer 部分源碼注釋
- 五、源碼及資源下載
參考文檔 :
- Timer 定時(shí)器 API 文檔
- TimerTask 定時(shí)器任務(wù) API 文檔
一、Timer 定時(shí)器基本使用
Timer 可用于執(zhí)行延遲任務(wù)或循環(huán)任務(wù) ; 下面是定時(shí)器最基本用法 ;
1 . Timer 定時(shí)器基本使用 :
- 創(chuàng)建 Timer 定時(shí)器 : 調(diào)用構(gòu)造函數(shù)創(chuàng)建定時(shí)器 Timer timer = new Timer() ;
- 分配 TimerTask 定時(shí)器任務(wù) : 調(diào)用定時(shí)器的 schedule 方法 , 為 Timer 定時(shí)器分配 TimerTask 定時(shí)器任務(wù) ;
2 . 定時(shí)器任務(wù)執(zhí)行規(guī)則 : Timer 執(zhí)行任務(wù)是 串行執(zhí)行 的 , 同一時(shí)間只能執(zhí)行一個(gè)任務(wù) ;
在下面的示例中
- 任務(wù) 1 在 1 秒之后執(zhí)行 , 在第 6 秒執(zhí)行完畢 ;
- 任務(wù) 2 在第 6 秒 , 任務(wù) 1 執(zhí)行完畢后 , 才開始執(zhí)行 , 在第 11 秒執(zhí)行完畢 ;
3 . 代碼示例 :
private void timer(){// Timer 可用于執(zhí)行延遲任務(wù)或循環(huán)任務(wù)Timer timer = new Timer();/*如果提交多個(gè) TimerTask 定時(shí)器任務(wù)需要等待之前的 定時(shí)器任務(wù) 執(zhí)行完成 , 才能執(zhí)行后面的任務(wù)TimerTask 實(shí)現(xiàn)了 Runnable 接口延遲 1 秒執(zhí)行任務(wù) 1 ( 任務(wù) 1 時(shí)長(zhǎng) 5 秒 )延遲 2 秒執(zhí)行任務(wù) 2 ( 任務(wù) 2 時(shí)長(zhǎng) 5 秒 )Timer 執(zhí)行任務(wù)是串行執(zhí)行的 , 同一時(shí)間只能執(zhí)行一個(gè)任務(wù)任務(wù) 1 在 1 秒之后執(zhí)行 , 在第 6 秒執(zhí)行完畢任務(wù) 2 在第 6 秒 , 任務(wù) 1 執(zhí)行完畢后 , 才開始執(zhí)行 , 在第 11 秒執(zhí)行完畢*/// 延遲 1 秒執(zhí)行任務(wù) 1timer.schedule(new TimerTask() {@Overridepublic void run() {Log.i(TAG, "延遲 1 秒執(zhí)行 5 秒的任務(wù) 1 開始執(zhí)行");try {Thread.sleep(5_000);} catch (InterruptedException e) {e.printStackTrace();}Log.i(TAG, "延遲 1 秒執(zhí)行 5 秒的任務(wù) 1 執(zhí)行完畢");}}, 1_000);// 延遲 2 秒執(zhí)行任務(wù) 2timer.schedule(new TimerTask() {@Overridepublic void run() {Log.i(TAG, "延遲 2 秒執(zhí)行 5 秒的任務(wù) 2 開始執(zhí)行");try {Thread.sleep(5_000);} catch (InterruptedException e) {e.printStackTrace();}Log.i(TAG, "延遲 2 秒執(zhí)行 5 秒的任務(wù) 2執(zhí)行完畢");}}, 2_000);}二、Timer 定時(shí)器常用用法
1 . Timer 定時(shí)器構(gòu)造函數(shù) :
① 創(chuàng)建默認(rèn)定時(shí)器 : 默認(rèn)以 “Timer-序列號(hào)” 作為定時(shí)器線程名稱 ;
public Timer() { this("Timer-" + serialNumber()); }② 創(chuàng)建守護(hù)線程定時(shí)器 : 指定定時(shí)器是否作為守護(hù)線程來執(zhí)行 , 定時(shí)器線程名稱是默認(rèn)名稱 ;
public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); }③ 創(chuàng)建定時(shí)器并指定定時(shí)器名稱 : 創(chuàng)建定時(shí)器 , 并 設(shè)置定時(shí)器線程名稱 ;
public Timer(String name) {thread.setName(name);thread.start();}④ 指定名稱并設(shè)置守護(hù)線程 : 同時(shí) 設(shè)置定時(shí)器名稱 , 并 設(shè)置定時(shí)器是否是守護(hù)線程 ;
public Timer(String name, boolean isDaemon) {thread.setName(name);thread.setDaemon(isDaemon);thread.start();}2 . 定時(shí)器調(diào)度方法 :
① 在指定一段時(shí)間后執(zhí)行定時(shí)器任務(wù) : 在 delay 毫秒后 , 執(zhí)行 TimerTask 定時(shí)器任務(wù) ;
public void schedule(TimerTask task, long delay) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");sched(task, System.currentTimeMillis()+delay, 0);}② 在指定的時(shí)間執(zhí)行定時(shí)器任務(wù) : 在某個(gè)指定的時(shí)間執(zhí)行 TimerTask 定時(shí)器任務(wù) ;
public void schedule(TimerTask task, Date time) { sched(task, time.getTime(), 0); }③ 在指定的時(shí)間執(zhí)行循環(huán)任務(wù) : 在 firstTime 時(shí)間執(zhí)行第一次 TimerTask 定時(shí)器任務(wù) , 之后每隔 period 毫秒的周期時(shí)間 , 循環(huán)執(zhí)行定時(shí)器任務(wù) ; 循環(huán)周期是 period 毫秒 ; 如果因?yàn)槟撤N原因?qū)е履承┎僮鞒霈F(xiàn)了延遲 , 那么后續(xù)操作也會(huì)跟著延遲 ;
public void schedule(TimerTask task, Date firstTime, long period) {if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, firstTime.getTime(), -period);}④ 延遲指定時(shí)間循環(huán)執(zhí)行任務(wù) : 延遲 delay 毫秒后 , 執(zhí)行第一次定時(shí)器任務(wù) , 然后每隔 period 毫秒 , 循環(huán)執(zhí)行定時(shí)器任務(wù) ; 循環(huán)周期是 period 毫秒 ; 如果因?yàn)槟撤N原因?qū)е履承┎僮鞒霈F(xiàn)了延遲 , 那么后續(xù)操作也會(huì)跟著延遲 ;
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);}⑤ 在指定的時(shí)間執(zhí)行循環(huán)任務(wù) : 在 firstTime 時(shí)間執(zhí)行第一次 TimerTask 定時(shí)器任務(wù) , 之后每隔 period 毫秒的周期時(shí)間 , 循環(huán)執(zhí)行定時(shí)器任務(wù) ; 循環(huán)周期是 period 毫秒 ; 如果因?yàn)槟撤N原因?qū)е履承┎僮鞒霈F(xiàn)了延遲 , 那么后續(xù)操作需要補(bǔ)償上述出現(xiàn)的延遲 ;
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);}⑥ 延遲指定時(shí)間循環(huán)執(zhí)行任務(wù) : 延遲 delay 毫秒后 , 執(zhí)行第一次定時(shí)器任務(wù) , 然后每隔 period 毫秒 , 循環(huán)執(zhí)行定時(shí)器任務(wù) ; 循環(huán)周期是 period 毫秒 ; 如果因?yàn)槟撤N原因?qū)е履承┎僮鞒霈F(xiàn)了延遲 , 那么后續(xù)操作需要補(bǔ)償上述出現(xiàn)的延遲 ;
public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period) {if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, firstTime.getTime(), period);}schedule 方法適用場(chǎng)景 : 適合用于平穩(wěn)執(zhí)行某種任務(wù) ; 穩(wěn)定性 > 準(zhǔn)確率 ;
scheduleAtFixedRate 方法適用場(chǎng)景 : 適合用于對(duì)絕對(duì)時(shí)間敏感的任務(wù) ; 準(zhǔn)確率 > 穩(wěn)定性 ;
三、Timer 源碼分析
在 Timer 中定義了 TimerThread thread 成員變量 , 該成員對(duì)象在創(chuàng)建對(duì)象時(shí)會(huì)自動(dòng)創(chuàng)建 ;
TimerThread 是定義在 Timer.java 文件中的類 , 是一個(gè)自定義線程類 ; 該幫助類實(shí)現(xiàn)了定時(shí)器任務(wù)的執(zhí)行線程 , 該線程中的定時(shí)器隊(duì)列等待任務(wù)到來 , 在合適的時(shí)間執(zhí)行定時(shí)器任務(wù) ; 調(diào)度需要重復(fù)執(zhí)行的任務(wù) ; 從任務(wù)隊(duì)列中 , 移出被取消的任務(wù) , 移出不需要循環(huán)執(zhí)行的任務(wù) ;
class TimerThread extends Thread{}在構(gòu)造函數(shù) public Timer(String name) 中 , 調(diào)用了該線程的 start() 方法 , 啟動(dòng)了該線程 ;
// 省略了無關(guān)代碼 public class Timer {private final TimerThread thread = new TimerThread(queue);public Timer(String name) {// 啟動(dòng) TimerThread 線程 thread.start();} }在 TimerThread 自定義線程中的 run() 方法中 , 主要是調(diào)用了 mainLoop() 方法 ; 該方法中是一個(gè)死循環(huán) , 從循環(huán)隊(duì)列中取出 TimerTask 定時(shí)器任務(wù) , 然后執(zhí)行 ; 必須等待前一個(gè)任務(wù)執(zhí)行完畢 , 才能執(zhí)行下一個(gè)任務(wù) ;
四、Timer 部分源碼注釋
// 省略了無關(guān)代碼 public class Timer {/*** 定時(shí)器線程 * 該 TimerThread thread 對(duì)象在創(chuàng)建對(duì)象時(shí)會(huì)自動(dòng)創(chuàng)建 */// Android-added: @ReachabilitySensitive@ReachabilitySensitiveprivate final TimerThread thread = new TimerThread(queue);// 生成序列號(hào) , 作為定時(shí)器線程的默認(rèn)名稱 private static int serialNumber() {return nextSerialNumber.getAndIncrement();}/*** 創(chuàng)建默認(rèn)定時(shí)器 , TimerThread thread 成員變量不作為守護(hù)線程*/public Timer() {this("Timer-" + serialNumber());}/*** 創(chuàng)建一個(gè)定時(shí)器 , 其關(guān)聯(lián)的 TimerThread thread 成員可以設(shè)置是否作為守護(hù)線程 ;* 如果該定時(shí)器用于用于調(diào)度重復(fù)性的維護(hù)活動(dòng) , 其守護(hù)線程會(huì)被調(diào)用 , * 應(yīng)用運(yùn)行期間必須調(diào)用守護(hù)線程 , * 但是上述操作不能影響應(yīng)用的生命周期 ; ** @param isDaemon 如果設(shè)置成 true , TimerThread thread 需要被設(shè)置成守護(hù)線程 */public Timer(boolean isDaemon) {this("Timer-" + serialNumber(), isDaemon);}/*** 創(chuàng)建一個(gè) Timer 定時(shí)器 , 并為其其關(guān)聯(lián)的線程設(shè)置指定的名稱 ; ** @param name 為 TimerThread 成員變量設(shè)置名稱*/public Timer(String name) {thread.setName(name);// 啟動(dòng) 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 設(shè)置關(guān)聯(lián)的定時(shí)器線程名稱* @param isDaemon 如果設(shè)置成 true 定時(shí)器線程將被設(shè)置成守護(hù)線程*/public Timer(String name, boolean isDaemon) {thread.setName(name);thread.setDaemon(isDaemon);thread.start();}}/*** TimerThread 是定義在 Timer.java 文件中的類 ; * * 該幫助類實(shí)現(xiàn)了定時(shí)器任務(wù)的執(zhí)行線程 , 該線程中的定時(shí)器隊(duì)列等待任務(wù)到來 , 在合適的時(shí)間執(zhí)行定時(shí)器任務(wù) ; * 調(diào)度需要重復(fù)執(zhí)行的任務(wù) ;* 從任務(wù)隊(duì)列中 , 移出被取消的任務(wù) , 移出不需要循環(huán)執(zhí)行的任務(wù) ; */ class TimerThread extends Thread {/*** 該標(biāo)志為會(huì) 被 reaper 設(shè)置成 false , 用于通知我們定時(shí)器對(duì)象已經(jīng)被銷毀 ; * 一旦該標(biāo)志位設(shè)置成 true , 并且任務(wù)隊(duì)列中沒有任務(wù)時(shí) , 沒有任務(wù)去執(zhí)行 , 這里就需要優(yōu)雅的關(guān)閉定時(shí)器 ; * 注意該字段由隊(duì)列管理器維護(hù) ; */boolean newTasksMayBeScheduled = true;/*** 任務(wù)隊(duì)列 , 調(diào)用 schedule 方法調(diào)度 TimerTask 定時(shí)器任務(wù) , 就是將任務(wù)加入到該隊(duì)列中 ; * 定時(shí)器任務(wù)隊(duì)列 , 在定時(shí)器線程中維護(hù)該隊(duì)列 , 而不是在定時(shí)器中維護(hù) , 這樣避免循環(huán)引用 ; * 否則定時(shí)器永遠(yuǎn)不會(huì)被回收 , 并且該線程永遠(yuǎn)不會(huì)消失 ; */private TaskQueue queue;TimerThread(TaskQueue queue) {this.queue = queue;}// run 方法中主要是調(diào)用 mainLoop() 方法 ; public void run() {try {mainLoop();} finally {// 如果定時(shí)器被取消 , 需要?dú)⑺涝摼€程synchronized(queue) {newTasksMayBeScheduled = false;queue.clear(); // 消除過時(shí)的引用}}}/*** 定時(shí)器輪詢 * 里面是一個(gè)死循環(huán) , 循環(huán)從人物隊(duì)列中取出 TimerTask 定時(shí)器任務(wù) , 然后執(zhí)行 ; * 必須等待前一個(gè)任務(wù)執(zhí)行完畢 , 才能執(zhí)行下一個(gè)任務(wù) */private void mainLoop() {while (true) {try {TimerTask task; // 定時(shí)器任務(wù) boolean taskFired; synchronized(queue) {// 如果隊(duì)列為空 , 一直阻塞 , 當(dāng)有任務(wù)進(jìn)來時(shí) , 解除阻塞while (queue.isEmpty() && newTasksMayBeScheduled)queue.wait();if (queue.isEmpty())break; // 如果隊(duì)列為空 , 就會(huì)退出循環(huán) , 終止該定時(shí)器 // 隊(duì)列不為空 , 查看第一個(gè) evt , 并執(zhí)行相應(yīng)操作 long currentTime, executionTime;task = queue.getMin();synchronized(task.lock) {if (task.state == TimerTask.CANCELLED) {queue.removeMin();continue; // 任務(wù)被取消 , 從隊(duì)列中移除}currentTime = System.currentTimeMillis();executionTime = task.nextExecutionTime;if (taskFired = (executionTime<=currentTime)) {if (task.period == 0) { // 該任務(wù)不需要循環(huán)執(zhí)行 , 從隊(duì)列中移除 queue.removeMin();task.state = TimerTask.EXECUTED;} else { // 該任務(wù)需要循環(huán)執(zhí)行 , 繼續(xù)調(diào)度queue.rescheduleMin(task.period<0 ? currentTime - task.period: executionTime + task.period);}}}if (!taskFired) // 等待前一個(gè)任務(wù)執(zhí)行完畢 , 如果之前的任務(wù)沒有執(zhí)行完畢 , 一直阻塞queue.wait(executionTime - currentTime);}if (taskFired) // 任務(wù)執(zhí)行完畢task.run();} catch(InterruptedException e) {}}} }
五、源碼及資源下載
源碼及資源下載地址 :
-
① GitHub 工程地址 : Android_Asynchronous
-
② MainActivity.java 主界面代碼地址 : MainActivity.java , 這是上述示例代碼位置 ; 僅做參考意義不大 ;
總結(jié)
以上是生活随笔為你收集整理的【Android 异步操作】Timer 定时器 ( Timer 与 TimerTask 基本使用 | Timer 定时器常用用法 | Timer 源码分析 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【运筹学】线性规划 单纯形法 阶段总结
- 下一篇: 【Kotlin】循环控制流 ( for