Handler
通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,但此时并没有运行,它需要CPU时间片。一旦得到CPU时间片,就会执行run()方法。run()的方法体称为线程体,它包含了要执行的这个线程的内容,run()方法运行结束,此线程也随即终止。
经过测试,这种倒计时方式锁屏之后20s左右就会失效(timer不会失效)
mHandler.postDelayed(new Runnable() { @Override public void run() { mHandler.postDelayed(this, 1000); } }, 1000);
Timer
一个Timer内部封装装了“一个Thread”和“一个TimerTask队列”,这个队列按照一定的方式将任务排队处理。封装的Thread在Timer的构造方法调用时被启动,这个Thread的run方法按照条件去循环这个TimerTask队列,然后调用TimerTask的run方法。 但是,如果CPU进入了休眠状态,那么这个thread将会因为失去CPU时间片而阻塞,从而造成我们需要的定时任务失效。
样例:自定义一个button,点击之后开启倒计时,并在button上显示剩余时间,同时要确保锁屏之后继续正常倒计时(handler锁屏会失效)。
创建一个TimeButton集成Button
public class TimeButton extends Button{
初始化定时器
private void initTimer(){ timer = new Timer(); task = new TimerTask() { @Override public void run() { mHandler.sendEmptyMessage(777); } }; }
启动定时器
public void start(){ initTimer(); timer.schedule(task , 0 , 1000);//延迟0s,每隔1s执行一次run方法定期刷新Button的textthis.setText(cycle + "");}
Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 777: cycle -= space; if(cycle > 0 ){ TimeButton.this.setText((cycle/1000) + "s"); }else { TimeButton.this.setText("重新获取"); onDestory(); } break; } } };
调用这个定时器:
/** * 进入后台保存当前的时间进度(当前activity进入后台调用) */ public void saveTime(long time){ mapTime.put("lastTimeOld" , time); lastTimeOld = cycle; } /** * 重新设置timer的显示的时间(当前activity进入前台调用) */ public void setNowTime(){ if(isStarted){ Long lastTime = mapTime.get("lastTimeOld"); int textTime = (int) (System.currentTimeMillis() - lastTime); if(textTime < lastTimeOld){ cycle = lastTimeOld - textTime; TimeButton.this.setText((cycle/1000) + "s"); }else { LogUtils.e("重新获取"); TimeButton.this.setText("重新获取"); onDestory(); setClickable(true); } } }/** * 点击事件 */case R.id.bt_time: timeButton.setClickable(false); timeButton.setTime(100000 , 1000).start();@Overrideprotected void onResume() { super.onResume(); timeButton.setNowTime();}@Overrideprotected void onStop() { super.onStop(); timeButton.saveTime(System.currentTimeMillis());}
CountDownTimer
这是Android自己封装的计时器,使用起来非常简单。但是经过测试,发现这种方式如果间隔设定为1s,每隔10s这个onTick()方法会少执行一次,及只执行了9次。
CountDownTimer timer = new CountDownTimer(20000 , 1000) {//参数(周期,间隔) //每隔时间间隔执行一次 @Override public void onTick(long millisUntilFinished) { tv_number.setText(millisUntilFinished/1000 + "s"); } //循环结束执行 @Override public void onFinish() { tv_number.setText("倒计时完了"); } };
它的底层实现源码:
public CountDownTimer(long millisInFuture, long countDownInterval) { mMillisInFuture = millisInFuture; mCountdownInterval = countDownInterval; }public synchronized final CountDownTimer start() { mCancelled = false; if (mMillisInFuture <= 0) { onFinish(); return this; } mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; mHandler.sendMessage(mHandler.obtainMessage(MSG)); return this; }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); } } } };
AlarmManager
AlarmManager将应用与服务分割开来后,使得应用程序开发者不用 关心具体的服务,而是直接通过AlarmManager来使用这种服务。这也许就是客户/服务模式的好处吧。当我们在开发android程序时,有些用户会使用service来管理一些后台的任务,如网络操作,或者间断处理数据等,这些用户需要在用户关闭程序,或者关闭屏幕后也能处理后台任务,可android手机为了节省电池使用,当用户关闭屏幕后,会将cpu置于休眠状态,当休眠状态启动,我们的服务就处于暂停状态了.为了斛决这个问题,android库里就提供了一个AlarmManager的库,AlarmManager是客户/服务模式的模式,也就是说AlarmManager是由系统来管理,而不是我们应用程序的一部分,也就是说AlarmManager是全局的,当我们的程序启用AlarmManager后,就给系统注册了一个服务,该服务会根据你设置的参数定时的向你的应用程序发送消息(注意,此消息以广播方式发送).
am = (AlarmManager)getSystemService(ALARM_SERVICE); int hour = 28 * 60 * 1000; long targetTime = System.currentTimeMillis() + hour; //其中SystemClock.elapsedRealtime()表示开机到现在的时间 Intent intent1 = new Intent(this , GetTokenCastReciver.class); pi = PendingIntent.getBroadcast(this, 0, intent1, 0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { am.setExact(AlarmManager.RTC_WAKEUP, targetTime, pi); }else { am.set(AlarmManager.RTC_WAKEUP, targetTime, pi); }