其實(shí)是定時(shí)周期性任務(wù)
本文將介紹五種 Android 實(shí)現(xiàn)倒計(jì)時(shí)的方法,對(duì)就是發(fā)送短信驗(yàn)證碼后的倒計(jì)時(shí)那種。其實(shí)就是執(zhí)行定時(shí)周期性的任務(wù)的五種方式,包括但不限于實(shí)現(xiàn)倒計(jì)時(shí)功能。這五種方式分別是:
- handler+postDelayed() 方式
- Timer + TimerTask + handler 方式
- ScheduledExecutorService + handler 方式
- RxJava 方式
- CountDownTimer 方式
其中 Timer 的方式實(shí)現(xiàn)定時(shí)任務(wù),這兒用來(lái)做倒計(jì)時(shí)是沒(méi)有問(wèn)題的。但是如果用來(lái)執(zhí)行周期任務(wù),恰好又有多個(gè)任務(wù),恰好兩個(gè)任務(wù)之間的時(shí)間間隔又比前一個(gè)任務(wù)執(zhí)行時(shí)間短就會(huì)發(fā)生定時(shí)不準(zhǔn)確的現(xiàn)象了。Timer 在執(zhí)行過(guò)程中如果任務(wù)跑出了異常,Timer 會(huì)停止所有的任務(wù)。Timer 執(zhí)行周期任務(wù)時(shí)依賴系統(tǒng)時(shí)間,系統(tǒng)時(shí)間的變化會(huì)引起 Timer 任務(wù)執(zhí)行的變化。額。。。。問(wèn)題貌似還挺多,所以我們用其他四種方式吧(手動(dòng)滑稽)。
自定義的 handler
幾種方式都會(huì)用到的 handler 放前面,避免 handler 引起的內(nèi)存泄露,使用 handler 時(shí)建議按如下方式讓 handler 持有一個(gè)當(dāng)前 Activity 的弱引用:
/**
* handler 持有當(dāng)前 Activity 的弱引用防止內(nèi)存泄露
*/
private static class LooperHandler extends Handler {
WeakReference<LoopersActivity> mWeakReference;
LooperHandler(LoopersActivity activity) {
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LoopersActivity loopersActivity = mWeakReference.get();
switch (msg.what) {
case 0:
loopersActivity.mTvValue.setText(String.valueOf(TOTAL_TIME_SEC));
if (TOTAL_TIME_SEC <= 0) {
loopersActivity.scheduled.shutdown();
loopersActivity.mTvValue.setText(loopersActivity.getResources().getString(R.string.done));
loopersActivity.mStart.setEnabled(true);
loopersActivity.mStart.setBackgroundColor(Color.parseColor("#f97e7e"));
}
TOTAL_TIME_SEC--;
break;
case 1:
loopersActivity.mTvValue.setText(String.valueOf(TOTAL_TIME_SEC));
if (TOTAL_TIME_SEC <= 0) {
loopersActivity.timer.cancel();
loopersActivity.timer = null;
loopersActivity.mTvValue.setText(loopersActivity.getResources().getString(R.string.done));
loopersActivity.mStart.setEnabled(true);
loopersActivity.mStart.setBackgroundColor(Color.parseColor("#f97e7e"));
}
TOTAL_TIME_SEC--;
break;
case 2:
loopersActivity.mHandler.postDelayed(loopersActivity.mRunnable, ONECE_TIME);
loopersActivity.mTvValue.setText(String.valueOf(TOTAL_TIME_SEC));
if (TOTAL_TIME_SEC <= 0) {
loopersActivity.mHandler.removeCallbacks(loopersActivity.mRunnable);
loopersActivity.timer = null;
loopersActivity.mTvValue.setText(loopersActivity.getResources().getString(R.string.done));
loopersActivity.mStart.setEnabled(true);
loopersActivity.mStart.setBackgroundColor(Color.parseColor("#f97e7e"));
}
TOTAL_TIME_SEC--;
break;
}
}
}
handler&postDelayed()
這個(gè)算是比較傳統(tǒng)的定時(shí)方式了,發(fā)送指定延時(shí)的消息來(lái)達(dá)到定時(shí)周期執(zhí)行的目的。
/**
* handler_postDelayed 方法實(shí)現(xiàn)
*/
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
Message msg = mHandler.obtainMessage(2);
mHandler.sendMessage(msg);
}
};
// handler+postDelayed 方式,反復(fù)發(fā)送延時(shí)消息
private void handlerPostDelayed() {
mHandler.postDelayed(mRunnable, ONECE_TIME);
}
Timer&TimerTask
通過(guò) timer 執(zhí)行周期延時(shí)的任務(wù),handler 中將計(jì)時(shí)信息更新,并在計(jì)時(shí)結(jié)束時(shí)結(jié)束 timer 的周期任務(wù)
/**
* TimkerTask 方式實(shí)現(xiàn)
*/
private Timer timer;
private void timerTask() {
timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
Message message = mHandler.obtainMessage(1);
mHandler.sendMessage(message);
}
};
timer.schedule(task, 0, ONECE_TIME);
}
ScheduledExecutorService
此方式中 handler 功能與 timer 方式一致
/**
* ScheduledExecutorService 方式實(shí)現(xiàn)
*/
private ScheduledExecutorService scheduled;
private void scheduledExecutorService() {
//初始化一個(gè)線程池大小為 1 的 ScheduledExecutorService
scheduled = new ScheduledThreadPoolExecutor(1);
mStart.setEnabled(false);//在發(fā)送數(shù)據(jù)的時(shí)候設(shè)置為不能點(diǎn)擊
mStart.setBackgroundColor(Color.GRAY);//背景色設(shè)為灰色
scheduled.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Message msg = mHandler.obtainMessage(0);
mHandler.sendMessage(msg);
}
}, 0, ONECE_TIME, TimeUnit.MILLISECONDS);
}
RxJava
此方法通過(guò) RxJava 的 interval 操作符來(lái)實(shí)現(xiàn)延時(shí)發(fā)送消息。接受消息的觀察者將計(jì)時(shí)信息更新到 textview 中。此方法主要涉及到一些 RxJava 操作符以及線程切換,代碼中也進(jìn)行了注釋。
/**
* RxJava 方式實(shí)現(xiàn)
*/
private void rxJava() {
final long count = TOTAL_TIME / 1000;
Observable.interval(0, 1, TimeUnit.SECONDS)//設(shè)置0延遲,每隔一秒發(fā)送一條數(shù)據(jù)
.take((int) (count + 1)) //設(shè)置總共發(fā)送的次數(shù)
.map(new Func1<Long, Long>() {//long 值是從小到大,倒計(jì)時(shí)需要將值倒置
@Override
public Long call(Long aLong) {
return count - aLong;
}
})
.subscribeOn(Schedulers.computation())
// doOnSubscribe 執(zhí)行線程由下游邏輯最近的 subscribeOn() 控制,下游沒(méi)有 subscribeOn() 則跟Subscriber 在同一線程執(zhí)行
//執(zhí)行計(jì)時(shí)任務(wù)前先將 button 設(shè)置為不可點(diǎn)擊
.doOnSubscribe(new Action0() {
@Override
public void call() {
mStart.setEnabled(false);//在發(fā)送數(shù)據(jù)的時(shí)候設(shè)置為不能點(diǎn)擊
mStart.setBackgroundColor(Color.GRAY);//背景色設(shè)為灰色
}
})
.observeOn(AndroidSchedulers.mainThread())//操作UI主要在UI線程
.subscribe(new Subscriber<Long>() {
@Override
public void onCompleted() {
mTvValue.setText(getResources().getString(R.string.done));
mStart.setEnabled(true);
mStart.setBackgroundColor(Color.parseColor("#f97e7e"));
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(Long aLong) { //接收到一條就是會(huì)操作一次UI
String value = String.valueOf(aLong);
mTvValue.setText(value);
}
});
}
CountDownTimer
這貨應(yīng)該是實(shí)現(xiàn)倒計(jì)時(shí)功能最簡(jiǎn)單的方式了,直接使用 Android SDK 中的 CountDownTimer 類
/**
* CountDownTimer 實(shí)現(xiàn)倒計(jì)時(shí)
*/
private CountDownTimer countDownTimer = new CountDownTimer(TOTAL_TIME, ONECE_TIME) {
@Override
public void onTick(long millisUntilFinished) {
String value = String.valueOf((int) (millisUntilFinished / 1000));
mTvValue.setText(value);
}
@Override
public void onFinish() {
mTvValue.setText(getResources().getString(R.string.done));
}
};
//調(diào)用 CountDownTimer 對(duì)象的 start() 方法開(kāi)始倒計(jì)時(shí),也不涉及到線程處理
countDownTimer.start();
最后
以上五種倒計(jì)時(shí)(定時(shí)周期任務(wù))方式就介紹完了,需要源碼可以去 MvpDemo 中查看,本 Demo 持續(xù)更新歡迎 star
覺(jué)得本文對(duì)你有幫助
簡(jiǎn)書(shū)PandaQ404
掘金PandaQ
GithubPandaQAQ