一凭需、Timer TimerTask
參考Java中的Timer和TimerTask在Android中的用法
在開(kāi)發(fā)中我們有時(shí)會(huì)有這樣的需求粒蜈,即在固定的每隔一段時(shí)間執(zhí)行某一個(gè)任務(wù)枯怖。比如UI上的控件需要隨著時(shí)間改變度硝,我們可以使用Java為我們提供的計(jì)時(shí)器的工具類(lèi)塘淑,即Timer和TimerTask存捺。
Timer是一個(gè)普通的類(lèi)捌治,其中有幾個(gè)重要的方法肖油;而TimerTask則是一個(gè)抽象類(lèi)森枪,其中有一個(gè)抽象方法run()县袱,類(lèi)似線程中的run()方法式散,我們使用Timer創(chuàng)建一個(gè)他的對(duì)象,然后使用這對(duì)象的schedule方法來(lái)完成這種間隔的操作编饺。
//true 說(shuō)明這個(gè)timer以daemon方式運(yùn)行(優(yōu)先級(jí)低透且,程序結(jié)束timer也自動(dòng)結(jié)束)
java.util.Timer timer = new java.util.Timer(true);
TimerTask task = new TimerTask() {
public void run() {
//每次需要執(zhí)行的代碼放到這里面石蔗。
}
};
//以下是幾種調(diào)度task的方法:
//time為Date類(lèi)型:在指定時(shí)間執(zhí)行一次。
timer.schedule(task, time);
//firstTime為Date類(lèi)型,period為long日熬,表示從firstTime時(shí)刻開(kāi)始,每隔period毫秒執(zhí)行一次毕荐。
timer.schedule(task, firstTime, period);
//delay 為long類(lèi)型:從現(xiàn)在起過(guò)delay毫秒執(zhí)行一次憎亚。
timer.schedule(task, delay);
//delay為long,period為long:從現(xiàn)在起過(guò)delay毫秒以后第美,每隔period毫秒執(zhí)行一次什往。
timer.schedule(task, delay, period);
//該任務(wù)每隔2秒更新主線程的UI(在主線程的TextView顯示最新的系統(tǒng)時(shí)間System.currentTimeMillis())别威。
package zhangphil.timertask;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.widget.TextView;
public class MainActivity extends Activity {
private Timer timer;
private TimerTask task;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tv = (TextView) findViewById(R.id.textView);
final int WHAT = 102;
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case WHAT:
tv.setText(msg.obj + "");
break;
}
}
};
task = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = WHAT;
message.obj = System.currentTimeMillis();
handler.sendMessage(message);
}
};
timer = new Timer();
// 參數(shù):
// 1000,延時(shí)1秒后執(zhí)行衫樊。
// 2000科侈,每隔2秒執(zhí)行1次task臀栈。
timer.schedule(task, 1000, 2000);
}
@Override
protected void onStop() {
super.onStop();
// 暫停
// timer.cancel();
// task.cancel();
}
}
schedule方法有三個(gè)參數(shù)
第一個(gè)參數(shù)就是TimerTask類(lèi)型的對(duì)象姑躲,我們實(shí)現(xiàn)TimerTask的run()方法就是要周期執(zhí)行的一個(gè)任務(wù)黍析;
第二個(gè)參數(shù)有兩種類(lèi)型屎开,第一種是long類(lèi)型奄抽,表示多長(zhǎng)時(shí)間后開(kāi)始執(zhí)行额划,另一種是Date類(lèi)型俊戳,表示從那個(gè)時(shí)間后開(kāi)始執(zhí)行品抽;
第三個(gè)參數(shù)就是執(zhí)行的周期圆恤,為long類(lèi)型盆昙。
schedule方法還有一種兩個(gè)參數(shù)的執(zhí)行重載,第一個(gè)參數(shù)仍然是TimerTask焊虏,第二個(gè)表示為long的形式表示多長(zhǎng)時(shí)間后執(zhí)行一次淡喜,為Date就表示某個(gè)時(shí)間后執(zhí)行一次。
Timer就是一個(gè)線程诵闭,使用schedule方法完成對(duì)TimerTask的調(diào)度炼团,多個(gè)TimerTask可以共用一個(gè)Timer澎嚣,也就是說(shuō)Timer對(duì)象調(diào)用一次schedule方法就是創(chuàng)建了一個(gè)線程,并且調(diào)用一次schedule后TimerTask是無(wú)限制的循環(huán)下去的易桃,使用Timer的cancel()停止操作。當(dāng)然同一個(gè)Timer執(zhí)行一次cancel()方法后锌俱,所有Timer線程都被終止晤郑。
若要在TimerTask中更新主線程UI,鑒于Android編程模型不允許在非主線程中更新主線程UI贸宏,因此需要結(jié)合Android的Handler實(shí)現(xiàn)在Java的TimerTask中更新主線程UI造寝。
二、ScheduledThreadPoolExecutor
public static ExecutorService newScheduledThreadPool(int corePoolSize){
return new ThreadPoolExecutor(corePoolSize,Integer.MAX_VALUE,0L,TimeUnit.SECONDS,new DelayedWorkQueue<Runnable>());
}
ScheduledThreadPoolExecutor核心線程數(shù)量固定吭练,非核心線程數(shù)沒(méi)有限制诫龙。主要用于執(zhí)行定時(shí)任務(wù)和具有固定周期的重復(fù)任務(wù)。
參考[Java 并發(fā)專(zhuān)題 : Timer的缺陷 用ScheduledExecutorService替代](http://blog.csdn.net/lmj623565791/article/details/27109467)
**以前在項(xiàng)目中也經(jīng)常使用定時(shí)器鲫咽,比如每隔一段時(shí)間清理項(xiàng)目中的一些垃圾文件赐稽,每個(gè)一段時(shí)間進(jìn)行數(shù)據(jù)清洗;然而Timer是存在一些缺陷的浑侥,因?yàn)門(mén)imer在執(zhí)行定時(shí)任務(wù)時(shí)只會(huì)創(chuàng)建一個(gè)線程,所以如果存在多個(gè)任務(wù)晰绎,且任務(wù)時(shí)間過(guò)長(zhǎng)寓落,超過(guò)了兩個(gè)任務(wù)的間隔時(shí)間,會(huì)發(fā)生一些缺陷.**
1.定義了兩個(gè)任務(wù)荞下,預(yù)計(jì)是第一個(gè)任務(wù)1s后執(zhí)行伶选,第二個(gè)任務(wù)3s后執(zhí)行
package com.zhy.concurrency.timer;
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest
{
private static long start;
public static void main(String[] args) throws Exception
{
TimerTask task1 = new TimerTask()
{
@Override
public void run()
{
System.out.println("task1 invoked ! "
+ (System.currentTimeMillis() - start));
try
{
Thread.sleep(3000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
};
TimerTask task2 = new TimerTask()
{
@Override
public void run()
{
System.out.println("task2 invoked ! "
+ (System.currentTimeMillis() - start));
}
};
Timer timer = new Timer();
start = System.currentTimeMillis();
timer.schedule(task1, 1000);
timer.schedule(task2, 3000);
}
}
運(yùn)行結(jié)果:
task1 invoked ! 1000
task2 invoked ! 4000
task2實(shí)際上是4后才執(zhí)行,正因?yàn)門(mén)imer內(nèi)部是一個(gè)線程尖昏,而任務(wù)1所需的時(shí)間超過(guò)了兩個(gè)任務(wù)間的間隔導(dǎo)致仰税。下面使用ScheduledThreadPool解決這個(gè)問(wèn)題:
package com.zhy.concurrency.timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolExecutorTest
{
private static long start;
public static void main(String[] args)
{
/**
* 使用工廠方法初始化一個(gè)ScheduledThreadPool
*/
ScheduledExecutorService newScheduledThreadPool = Executors
.newScheduledThreadPool(2);
TimerTask task1 = new TimerTask()
{
@Override
public void run()
{
try
{
System.out.println("task1 invoked ! "
+ (System.currentTimeMillis() - start));
Thread.sleep(3000);
} catch (Exception e)
{
e.printStackTrace();
}
}
};
TimerTask task2 = new TimerTask()
{
@Override
public void run()
{
System.out.println("task2 invoked ! "
+ (System.currentTimeMillis() - start));
}
};
start = System.currentTimeMillis();
newScheduledThreadPool.schedule(task1, 1000, TimeUnit.MILLISECONDS);
newScheduledThreadPool.schedule(task2, 3000, TimeUnit.MILLISECONDS);
}
}
2.如果TimerTask拋出RuntimeException,Timer會(huì)停止所有任務(wù)的運(yùn)行:
package com.zhy.concurrency.timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class ScheduledThreadPoolDemo01
{
public static void main(String[] args) throws InterruptedException
{
final TimerTask task1 = new TimerTask()
{
@Override
public void run()
{
throw new RuntimeException();
}
};
final TimerTask task2 = new TimerTask()
{
@Override
public void run()
{
System.out.println("task2 invoked!");
}
};
Timer timer = new Timer();
timer.schedule(task1, 100);
timer.scheduleAtFixedRate(task2, new Date(), 1000);
}
}
上面有兩個(gè)任務(wù)抽诉,任務(wù)1拋出一個(gè)運(yùn)行時(shí)的異常陨簇,任務(wù)2周期性的執(zhí)行某個(gè)操作,輸出結(jié)果:
task2 invoked!
Exception in thread "Timer-0" java.lang.RuntimeException
at com.zhy.concurrency.timer.ScheduledThreadPoolDemo01$1.run(ScheduledThreadPoolDemo01.java:24)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
由于任務(wù)1的一次迹淌,任務(wù)2也停止運(yùn)行了河绽。。唉窃。下面使用ScheduledExecutorService解決這個(gè)問(wèn)題:
package com.zhy.concurrency.timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolDemo01
{
public static void main(String[] args) throws InterruptedException
{
final TimerTask task1 = new TimerTask()
{
@Override
public void run()
{
throw new RuntimeException();
}
};
final TimerTask task2 = new TimerTask()
{
@Override
public void run()
{
System.out.println("task2 invoked!");
}
};
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
pool.schedule(task1, 100, TimeUnit.MILLISECONDS);
pool.scheduleAtFixedRate(task2, 0 , 1000, TimeUnit.MILLISECONDS);
}
}
代碼基本一致耙饰,但是ScheduledExecutorService可以保證,task1出現(xiàn)異常時(shí)纹份,不影響task2的運(yùn)行:
task2 invoked!
task2 invoked!
task2 invoked!
task2 invoked!
task2 invoked!
#####三苟跪、AlarmManager
>Java的Timer類(lèi)可以用來(lái)計(jì)劃需要循環(huán)執(zhí)行的任務(wù)廷痘。簡(jiǎn)單的說(shuō),一個(gè)Timer內(nèi)部封裝裝了“一個(gè)Thread”和“一個(gè)TimerTask隊(duì)列”件已,這個(gè)隊(duì)列按照一定的方式將任務(wù)排隊(duì)處理笋额。封裝的Thread在Timer的構(gòu)造方法調(diào)用時(shí)被啟動(dòng),這個(gè)Thread的run方法按照條件去循環(huán)這個(gè)TimerTask隊(duì)列拨齐,然后調(diào)用TimerTask的run方法鳞陨。
但是,**如果CPU進(jìn)入了休眠狀態(tài)瞻惋,那么這個(gè)thread將會(huì)因?yàn)槭PU時(shí)間片而阻塞厦滤,從而造成我們需要的定時(shí)任務(wù)失效**。上述定時(shí)任務(wù)失效的場(chǎng)景分析:假設(shè)定時(shí)任務(wù)的條件是到了時(shí)間xx:yy才能執(zhí)行歼狼,但由于cpu休眠造成線程阻塞的關(guān)系掏导,當(dāng)前系統(tǒng)時(shí)間超過(guò)了這個(gè)時(shí)間,即便CPU從終端中恢復(fù)了羽峰,那么由于條件不滿(mǎn)足趟咆,定時(shí)任務(wù)在這一次自然就失效了。
解決方案是:它**需要用WakeLock讓CPU 保持喚醒狀態(tài)梅屉。那么問(wèn)題就來(lái)了值纱,這樣會(huì)大量消耗手機(jī)電量(CPU喚醒和屏幕喚醒不是同一概念)**,大大減短手機(jī)待機(jī)時(shí)間坯汤。這種方式不能滿(mǎn)足我們的需求虐唠。
注:TimerTask實(shí)現(xiàn)Runnable接口,但它的run方法只是簡(jiǎn)單的在Timer中封裝的Thread中被調(diào)用惰聂,并未將其放在其它線程中執(zhí)行疆偿。也就是說(shuō)timer是單線程執(zhí)行的,那么問(wèn)題來(lái)了搓幌,為何要這么費(fèi)勁的封裝一個(gè)Runnable接口又不進(jìn)行多線程調(diào)用杆故?我的答案是,老外只是想要表示它是可執(zhí)行的方法溉愁。
關(guān)于休眠处铛,可以參考
[Android 關(guān)于休眠引發(fā)的幾個(gè)血案](http://www.ithtw.com/437.html)
[Android手機(jī)休眠后時(shí)間不準(zhǔn)確的解決方案](http://blog.csdn.net/t12x3456/article/details/7826811)
AlarmManager是Android 系統(tǒng)封裝的用于管理RTC的模塊,RTC(Real Time Clock) 是一個(gè)獨(dú)立的硬件時(shí)鐘拐揭,可以在 CPU 休眠時(shí)正常運(yùn)行罢缸,在預(yù)設(shè)的時(shí)間到達(dá)時(shí),通過(guò)中斷喚醒CPU投队。這意味著枫疆,如果我們用 AlarmManager 來(lái)定時(shí)執(zhí)行任務(wù),CPU 可以正常的休眠敷鸦,只有在需要運(yùn)行任務(wù)時(shí)醒來(lái)一段很短的時(shí)間息楔。
以下參考[Android基礎(chǔ)入門(mén)教程——10.5 AlarmManager(鬧鐘服務(wù))](http://blog.csdn.net/coder_pig/article/details/49423531)
**核心流程如下:**
* `AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); `
獲得系統(tǒng)提供的AlarmManager服務(wù)的對(duì)象
* Intent設(shè)置要啟動(dòng)的組件:
`Intent intent = new Intent(MainActivity.this, ClockActivity.class);`
* PendingIntent對(duì)象設(shè)置動(dòng)作,啟動(dòng)的是Activity還是Service,又或者是廣播!
`PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);`
* 調(diào)用AlarmManager的set( )方法設(shè)置單次鬧鐘的鬧鐘類(lèi)型,啟動(dòng)時(shí)間以及PendingIntent對(duì)象!
`alarmManager.set(AlarmManager.RTC_WAKEUP,c.getTimeInMillis(), pi);`
//10秒鐘后執(zhí)行一個(gè)任務(wù)
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);
**相關(guān)方法:**
* set(int type,long startTime,PendingIntent pi):一次性鬧鐘
* setRepeating(int type寝贡,long startTime,long intervalTime值依,PendingIntent pi):
重復(fù)性鬧鐘,和3有區(qū)別,3鬧鐘間隔時(shí)間不固定
* setInexactRepeating(int type圃泡,long startTime,long intervalTime,PendingIntent pi):
重復(fù)性鬧鐘愿险,時(shí)間不固定
* cancel(PendingIntent pi):取消AlarmManager的定時(shí)服務(wù)
* getNextAlarmClock():得到下一個(gè)鬧鐘颇蜡,返回值A(chǔ)larmManager.AlarmClockInfo
* setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation)
和set方法類(lèi)似,這個(gè)鬧鐘運(yùn)行在系統(tǒng)處于低電模式時(shí)有效
* setExact(int type, long triggerAtMillis, PendingIntent operation):
在規(guī)定的時(shí)間精確的執(zhí)行鬧鐘辆亏,比set方法設(shè)置的精度更高
* setTime(long millis):設(shè)置系統(tǒng)墻上的時(shí)間
* setTimeZone(String timeZone):設(shè)置系統(tǒng)持續(xù)的默認(rèn)時(shí)區(qū)
* setWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation):
設(shè)置一個(gè)鬧鐘在給定的時(shí)間窗觸發(fā)风秤。類(lèi)似于set,該方法允許應(yīng)用程序精確地控制操作系統(tǒng)調(diào) 整鬧鐘觸發(fā)時(shí)間的程度扮叨。
**關(guān)鍵參數(shù)講解:**
* Type(鬧鐘類(lèi)型):
有五個(gè)可選值:
* AlarmManager.ELAPSED_REALTIME:
鬧鐘在手機(jī)睡眠狀態(tài)下不可用缤弦,該狀態(tài)下鬧鐘使用相對(duì)時(shí)間(相對(duì)于系統(tǒng)啟動(dòng)開(kāi)始),狀態(tài)值為3;
* AlarmManager.ELAPSED_REALTIME_WAKEUP
鬧鐘在睡眠狀態(tài)下會(huì)喚醒系統(tǒng)并執(zhí)行提示功能彻磁,該狀態(tài)下鬧鐘也使用相對(duì)時(shí)間碍沐,狀態(tài)值為2;
* AlarmManager.RTC
鬧鐘在睡眠狀態(tài)下不可用衷蜓,該狀態(tài)下鬧鐘使用絕對(duì)時(shí)間累提,即當(dāng)前系統(tǒng)時(shí)間,狀態(tài)值為1磁浇;
* AlarmManager.RTC_WAKEUP
表示鬧鐘在睡眠狀態(tài)下會(huì)喚醒系統(tǒng)并執(zhí)行提示功能斋陪,該狀態(tài)下鬧鐘使用絕對(duì)時(shí)間,狀態(tài)值為0;
* AlarmManager.POWER_OFF_WAKEUP
表示鬧鐘在手機(jī)關(guān)機(jī)狀態(tài)下也能正常進(jìn)行提示功能扯夭,所以是5個(gè)狀態(tài)中用的最多的狀態(tài)之一,該狀態(tài)下鬧鐘也是用絕對(duì)時(shí)間鞍匾,狀態(tài)值為4交洗;不過(guò)本狀態(tài)好像受SDK版本影響,某些版本并不支持橡淑;
* startTime:鬧鐘的第一次執(zhí)行時(shí)間构拳,以毫秒為單位,可以自定義時(shí)間梁棠,不過(guò)一般使用當(dāng)前時(shí)間置森。 需要注意的是,本屬性與第一個(gè)屬性(type)密切相關(guān),如果第一個(gè)參數(shù)對(duì)應(yīng)的鬧鐘使用的是相對(duì)時(shí)間 (ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那么本屬性就得使用相對(duì)時(shí)間相對(duì)于系統(tǒng)啟動(dòng)時(shí)間來(lái)說(shuō)),比如當(dāng)前時(shí)間就表示為:SystemClock.elapsedRealtime()符糊; 如果第一個(gè)參數(shù)對(duì)應(yīng)的鬧鐘使用的是絕對(duì)時(shí)間(RTC凫海、RTC_WAKEUP、POWER_OFF_WAKEUP), 那么本屬性就得使用絕對(duì)時(shí)間男娄,比如當(dāng)前時(shí)間就表示 為:System.currentTimeMillis()行贪。
* intervalTime:表示兩次鬧鐘執(zhí)行的間隔時(shí)間,也是以毫秒為單位.
* PendingIntent:綁定了鬧鐘的執(zhí)行動(dòng)作漾稀,比如發(fā)送一個(gè)廣播、給出提示等等建瘫。
PendingIntent是Intent的封裝類(lèi)崭捍。需要注意的是,
* 如果是通過(guò)啟動(dòng)服務(wù)來(lái)實(shí)現(xiàn)鬧鐘提示的話(huà)啰脚,PendingIntent對(duì)象的獲取就應(yīng)該采用Pending.getService (Context c,int i,Intent intent,int j)方法殷蛇;
* 如果是通過(guò)廣播來(lái)實(shí)現(xiàn)鬧鐘提示的話(huà),PendingIntent對(duì)象的獲取就應(yīng)該采用 PendingIntent.getBroadcast (Context c,int i,Intent intent,int j)方法橄浓;
* 如果是采用Activity的方式來(lái)實(shí)現(xiàn)鬧鐘提示的話(huà)粒梦,PendingIntent對(duì)象的獲取就應(yīng)該采用
PendingIntent.getActivity(Context c,int i,Intent intent,int j)方法。
如果這三種方法錯(cuò)用了的話(huà)贮配,雖然不會(huì)報(bào)錯(cuò)谍倦,但是看不到鬧鐘提示效果。
下面是一個(gè)每隔一小時(shí)就會(huì)在后臺(tái)執(zhí)行定時(shí)任務(wù)的服務(wù)泪勒。
public class LongRunningService extends Service{
public IBinder onBind(Intent intent){
return null;
}
public int onStartCommand(Intent intent, int flags, int startId){
new Thread(new Runnable(){
public void run(){
Log.d("LongRunningService","executed at"+new Date().toString());
}
}).start();
}
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int anHour = 60 * 60 * 1000;
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
Intent i = new Intent(this,AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this,0,i,0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);
return super.onStartCommand(intent,flags,startId);
}
public class AlarmReceiver extends BroadcastReceiver{
public void onReceive(Context context, Intent intent){
Intent i = new Intent(context, LongRunningService.class);
context.startService(i);
}
}
public class MainActivity extends Activity{
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Intent intent = new Intent(this,LongRunningService);
startService(intent);
}
}
#####四昼蛀、CountDownTimer
參考
[Android實(shí)現(xiàn)倒計(jì)時(shí)之使用CountDownTimer](http://blog.csdn.net/qq_20785431/article/details/51571300)
[[Android] CountDownTimer 簡(jiǎn)單用法與源碼解析](http://blog.qiji.tech/archives/7485)
>在開(kāi)發(fā)中會(huì)經(jīng)常用到倒計(jì)時(shí)這個(gè)功能,包括給手機(jī)發(fā)送驗(yàn)證碼等等圆存,之前我的做法都是使用Handler + Timer +TimerTask來(lái)實(shí)現(xiàn)叼旋,現(xiàn)在發(fā)現(xiàn)了這個(gè)類(lèi),果斷拋棄之前的做法沦辙,相信還是有很多人和我一樣一開(kāi)始不知道Android已經(jīng)幫我們封裝好了一個(gè)叫CountDownTimer的類(lèi)夫植。`CountDownTimer timer = new CountDownTimer(10000,1000);`以毫秒為單位,第一個(gè)參數(shù)是指從開(kāi)始調(diào)用start()方法到倒計(jì)時(shí)完成的時(shí)候onFinish()方法被調(diào)用這段時(shí)間的毫秒數(shù)油讯,也就是倒計(jì)時(shí)總的時(shí)間详民;第二個(gè)參數(shù)表示間隔多少毫秒調(diào)用一次 onTick方法,例如間隔1000毫秒陌兑。在調(diào)用的時(shí)候直接使用`timer.start();`
//共有4個(gè)方法沈跨,前兩個(gè)抽象方法需要重寫(xiě)
public abstract void onTick(long millisUntilFinished);//固定間隔調(diào)用
public abstract void onFinish();//倒計(jì)時(shí)完成時(shí)被調(diào)用
public synchronized final void cancel();//取消倒計(jì)時(shí),當(dāng)再次啟動(dòng)會(huì)重新開(kāi)始倒計(jì)時(shí)
public synchronized final CountDownTimer start();//啟動(dòng)倒計(jì)時(shí)
//該類(lèi)的成員變量與成員函數(shù)均較少兔综,功能實(shí)現(xiàn)的關(guān)鍵部分在于 mHandler饿凛,下面看 mHandler 的源碼:
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};

看一個(gè)使用例子:
package com.per.countdowntimer;
import android.app.Activity;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView mTvShow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvShow = (TextView) findViewById(R.id.show);
}
/**
* 取消倒計(jì)時(shí)
* @param v
*/
public void oncancel(View v) {
timer.cancel();
}
/**
* 開(kāi)始倒計(jì)時(shí)
* @param v
*/
public void restart(View v) {
timer.start();
}
private CountDownTimer timer = new CountDownTimer(10000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
mTvShow.setText((millisUntilFinished / 1000) + "秒后可重發(fā)");
}
@Override
public void onFinish() {
mTvShow.setEnabled(true);
mTvShow.setText("獲取驗(yàn)證碼");
}
};
}
#####五、new Handler().postDelayed()
該方法就是利用我們常說(shuō)的消息處理器软驰。該方法原理就是在主線程中創(chuàng)建一個(gè)Handler消息處理器涧窒,然后利用其中的一個(gè)postDelayed(Runnable r, long delayMillis)方法,該方法第一個(gè)參數(shù)需要傳入一個(gè)Runnable接口锭亏,并實(shí)現(xiàn)run()方法纠吴,第二個(gè)參數(shù)就是延遲多少時(shí)間將run()方法中的代碼通過(guò)一個(gè)消息發(fā)送給消息隊(duì)列,然后在主線程中執(zhí)行這個(gè)消息中的代碼慧瘤,即是run方法中的代碼呜象,從而實(shí)現(xiàn)在主線程中更新界面UI膳凝。
貼代碼吧:
new Handler().postDelayed(new Runnable() {//在當(dāng)前線程(也即主線程中)開(kāi)啟一個(gè)消息處理器,并在3秒后在主線程中執(zhí)行恭陡,從而來(lái)更新UI
@Override
public void run() {
//有關(guān)更新UI的代碼
}
}, 3000);//3秒后發(fā)送