Android實(shí)現(xiàn)一個(gè)完美的倒計(jì)時(shí)功能
一. 已有倒計(jì)時(shí)方案存在的問(wèn)題
在開發(fā)倒計(jì)時(shí)功能時(shí)往往我們會(huì)為了方便直接使用CountDownTimer或者使用Handler做延時(shí)來(lái)實(shí)現(xiàn),當(dāng)然CountDownTimer內(nèi)部封裝也是使用的Handler。
如果只是做次數(shù)很少的倒計(jì)時(shí)或者不需要精確的倒計(jì)時(shí)邏輯那倒沒(méi)關(guān)系,比如說(shuō)我只要倒計(jì)時(shí)10秒,或者我大概5分鐘請(qǐng)求某個(gè)接口
但是如果是需要做精確的倒計(jì)時(shí)操作,比如說(shuō)手機(jī)發(fā)送驗(yàn)證碼60秒,那使用現(xiàn)有的倒計(jì)時(shí)方案就會(huì)存在問(wèn)題??赡苡行┡笥褯](méi)有注意到這一點(diǎn),下面我們就來(lái)簡(jiǎn)單分析一下現(xiàn)有倒計(jì)時(shí)的問(wèn)題。
1. CountDownTimer
這個(gè)可能是用得最多的,因?yàn)榉奖懵?。但其?shí)倒計(jì)時(shí)每一輪倒計(jì)時(shí)完之后都是存在誤差的,如果看過(guò)CountDownTimer的源碼你就會(huì)知道,他的內(nèi)部是有做校準(zhǔn)操作的。(源碼很簡(jiǎn)單這里就不分析了)
但是如果你認(rèn)真的測(cè)試過(guò)CountDownTimer,你就會(huì)發(fā)現(xiàn),即便它內(nèi)部有做校準(zhǔn)操作,他的沒(méi)一輪都是有偏差,只是他最后一次倒計(jì)時(shí)完之后的總共時(shí)間和開始倒計(jì)時(shí)的時(shí)間相比沒(méi)偏差。
什么意思呢,意思就是1秒,2.050秒,3.1秒......,這樣的每輪偏差,導(dǎo)致他會(huì)出現(xiàn)10.95秒,下一次12秒的情況,那它的回調(diào)中如果你直接做取整就會(huì)出現(xiàn)少一秒的情況,但實(shí)際是沒(méi)少的。
這只是其中的一個(gè)問(wèn)題,你可以不根據(jù)它的回調(diào)做展示,自己用一個(gè)整形累加做展示也能解決。但是他還有個(gè)問(wèn)題,有概率直接出現(xiàn)跳秒,就是比如3秒,下次直接5秒,這是實(shí)際的跳秒,是少了一次回調(diào)的那種。
跳秒導(dǎo)致你如果直接使用它可能會(huì)大問(wèn)題,你可能自測(cè)的時(shí)候沒(méi)發(fā)現(xiàn),到時(shí)一上線應(yīng)用在用戶那概率跳秒,那就蛋疼了。
2. Handler
不搞這么多花里胡哨的,直接使用Handler來(lái)實(shí)現(xiàn),會(huì)有什么問(wèn)題。
因?yàn)橹苯邮褂胔andler來(lái)實(shí)現(xiàn),沒(méi)有校準(zhǔn)操作,每次循環(huán)會(huì)出現(xiàn)幾毫秒的誤差,雖然比CountDownTimer的十幾毫秒的誤差要好,但是在基數(shù)大的倒計(jì)時(shí)情況下誤差會(huì)累計(jì),導(dǎo)致最終結(jié)果和現(xiàn)實(shí)時(shí)間差幾秒誤差,時(shí)間越久,誤差越大
3. Timer
直接使用Timer也一樣,只不過(guò)他每輪的誤差更小,幾輪才有1毫秒的誤差,但是沒(méi)有校準(zhǔn)還是會(huì)出現(xiàn)誤差累計(jì),時(shí)間越久誤差越大。
二. 自己封裝倒計(jì)時(shí)
既然無(wú)法直接使用原生的,那我們就自己做一個(gè)。
我們基于Handler進(jìn)行封裝,從上面可以看出主要為了解決兩個(gè)問(wèn)題,時(shí)間校準(zhǔn)和跳秒。自己寫一個(gè)CountDownTimer
public class CountDownTimer { private int mTimes; private int allTimes; private final long mCountDownInterval; private final Handler mHandler; private OnTimerCallBack mCallBack; private boolean isStart; private long startTime; public CountDownTimer(int times, long countDownInterval){ this.mTimes = times; this.mCountDownInterval = countDownInterval; mHandler = new Handler(); } public synchronized void start(OnTimerCallBack callBack){ this.mCallBack = callBack; if (isStart || mCountDownInterval <= 0){ return; } isStart = true; if (callBack != null){ callBack.onStart(); } startTime = SystemClock.elapsedRealtime(); if (mTimes <= 0){ finishCountDown(); return; } allTimes = mTimes; mHandler.postDelayed(runnable, mCountDownInterval); } private final Runnable runnable = new Runnable() { @Override public void run() { mTimes--; if (mTimes > 0){ if (mCallBack != null){ mCallBack.onTick(mTimes); } long nowTime = SystemClock.elapsedRealtime(); long delay = (nowTime - startTime) - (allTimes - mTimes) * mCountDownInterval; // 處理跳秒 while (delay > mCountDownInterval){ mTimes --; if (mCallBack != null){ mCallBack.onTick(mTimes); } delay -= mCountDownInterval; if (mTimes <= 0){ finishCountDown(); return; } } mHandler.postDelayed(this, 1000 - delay); }else { finishCountDown(); } } }; private void finishCountDown(){ if (mCallBack != null){ mCallBack.onFinish(); } isStart = false; } public void cancel(){ mHandler.removeCallbacksAndMessages(null); isStart = false; } public interface OnTimerCallBack{ void onStart(); void onTick(int times); void onFinish(); } }
思路就是在倒計(jì)時(shí)開始前獲取一次SystemClock.elapsedRealtime(),沒(méi)輪倒計(jì)時(shí)再獲取一次SystemClock.elapsedRealtime()相減得到誤差,根據(jù)delay校準(zhǔn)。然后使用while循壞來(lái)處理跳秒的操作,與原生的CountDownTimer不同,這里如果跳了多少秒,就會(huì)返回多少次回調(diào)。
總結(jié)
到此這篇關(guān)于利用Android實(shí)現(xiàn)一個(gè)完美的倒計(jì)時(shí)功能的文章就介紹到這了,更多相關(guān)Android倒計(jì)時(shí)功能內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android中實(shí)現(xiàn)多行、水平滾動(dòng)的分頁(yè)的Gridview實(shí)例源碼
如果單行水平滾動(dòng),可以用Horizontalscrollview實(shí)現(xiàn)。如果是多行水平滾動(dòng),則結(jié)合Gridview(一般是垂直滾動(dòng)的)和Horizontalscrollview實(shí)現(xiàn)2013-06-06Android 使用SharePerference判斷是否為第一次登陸的實(shí)現(xiàn)代碼
很多app中在第一次安裝登陸時(shí)會(huì)有引導(dǎo)歡迎界面,第二次打開時(shí)就不再顯示引導(dǎo)頁(yè)面。這個(gè)怎么實(shí)現(xiàn)呢?下面小編給大家介紹下使用SharePerference判斷是否為第一次登陸的實(shí)現(xiàn)代碼,需要的的朋友參考下吧2017-03-03Android編程實(shí)現(xiàn)擦除Bitmap中某一塊的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)擦除Bitmap中某一塊的方法,涉及Android操作Bitmap顏色像素值調(diào)整的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Android實(shí)現(xiàn)水波紋擴(kuò)散效果的實(shí)例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)水波紋擴(kuò)散效果的實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Android單選按鈕對(duì)話框用法實(shí)例分析
這篇文章主要介紹了Android單選按鈕對(duì)話框用法,以完整實(shí)例形式分析布局及對(duì)話框類的相關(guān)使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09詳解Kotlin Android開發(fā)中的環(huán)境配置
這篇文章主要介紹了詳解Kotlin Android開發(fā)中的環(huán)境配置的相關(guān)資料,需要的朋友可以參考下2017-06-06