在Android開發中,有時須要使用倒計時功能,在Android系統中提供了一個倒計時的抽象類來輔助倒計時行爲。android
public class CountDownTimeActivity extends Activity implements OnClickListener { TextView mTextView; Button mButton1; Button mButton2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.countdown); mTextView = (TextView)findViewById(R.id.textView1); mButton1 = (Button)findViewById(R.id.button1); mButton2 = (Button)findViewById(R.id.button2); mButton1.setOnClickListener(this); mButton2.setOnClickListener(this); } CountDownTimer timer = new CountDownTimer(40000,1000) { //定義40秒,每一秒執行一次 @Override public void onTick(long millisUntilFinished) { mTextView.setText("seconds remaining: " + millisUntilFinished / 1000); //millisUntilFinished不是精確值,須要進行計算millisUntilFinished / 1000 } @Override public void onFinish() { mTextView.setText("done!"); } }; @Override public void onClick(View v) { switch(v.getId()){ case R.id.button1: timer.start(); break; case R.id.button2: timer.cancel(); //取消後中止運行。下次還會從新開始,而不是接着開始 break; } } }
這個類有點缺陷,就是不能暫停後再次接着繼續使用,爲此,須要自定義一個相似的倒計時計時器併發
package android.os; import android.util.Log; public abstract class CountDownTimer { private final long mMillisInFuture; private final long mCountdownInterval; private long mStopTimeInFuture; private long millisUntilFinished = 0; private boolean isPause = false; public CountDownTimer(long millisInFuture, long countDownInterval) { mMillisInFuture = millisInFuture; mCountdownInterval = countDownInterval; } public final void cancel() { mHandler.removeMessages(MSG); } public final void pause() { cancel(); isPause = true; } public synchronized final CountDownTimer start() { if (mMillisInFuture <= 0) { onFinish(); return this; } mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; if(isPause) { mStopTimeInFuture = mStopTimeInFuture - millisUntilFinished; isPause = false; } mHandler.sendMessage(mHandler.obtainMessage(MSG)); return this; } public abstract void onTick(long millisUntilFinished); public abstract void onFinish(); private static final int MSG = 1; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { synchronized (CountDownTimer.this) { final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime(); if (millisLeft <= 0) { onFinish(); millisUntilFinished = 0; } else if (millisLeft < mCountdownInterval) { sendMessageDelayed(obtainMessage(MSG), millisLeft); } else { long lastTickStart = SystemClock.elapsedRealtime(); onTick(millisLeft); millisUntilFinished = millisLeft; long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime(); while (delay < 0) delay += mCountdownInterval; sendMessageDelayed(obtainMessage(MSG), delay); } } } }; }
2、核心源碼解析async
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) { // 剩餘時間小於一次時間間隔的時候,再也不通知,只是延遲一下 sendMessageDelayed(obtainMessage(MSG), millisLeft); } else { long lastTickStart = SystemClock.elapsedRealtime(); onTick(millisLeft); // 處理用戶onTick執行的時間 long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime(); // 特殊狀況:用戶的onTick方法花費的時間比interval長,那麼直接跳轉到下一次interval while (delay < 0) delay += mCountdownInterval; sendMessageDelayed(obtainMessage(MSG), delay); } } } };
經過源碼可知,CountDownTimer採用的是handler機制,經過sendMessageDelayed延遲發送一條message到主線程的looper中,而後在自身中收到以後判斷剩餘時間,併發出相關回調,而後再次發出message的方式。以前實現這種倒計時是經過asynctask,在線程中經過Thread.sleep來實現,經過asyntask的cancel來實現取消,經過構造asynctask傳入接口的實現來onTick的相似功能。這個CountDownTimer默認是在當前looper當中,能夠是在UI線程也能夠是在非UI線程中執行,若是在UI線程中執行,那是否是會稍微加劇UI線程的負擔?ide
此外、主線程隊列阻塞、倒計時的準確度如何?oop
try doing it!this