参考的Java中的定时器和TimerTask的在安卓中的用法
在开发中我们有时会有这样的需求,即在固定的每隔一段时间执行某一个任务。比如UI上的控件需要随着时间改变,我们可以使用的Java为我们提供的计时器的工具类,即定时器和TimerTask的。
计时器是一个普通的类,其中有几个重要的方法;而TimerTask的则是一个抽象类,其中有一个抽象方法run()中,类似线程中的运行()方法,我们使用计时器创建一个他的对象,然后使用这对象的时间表方法来完成这种间隔的操作。
//true 说明这个timer以daemon方式运行(优先级低,程序结束timer也自动结束)
java.util.Timer timer = new java.util.Timer(true);
TimerTask task = new TimerTask() {
public void run() {
//每次需要执行的代码放到这里面。
}
};
//以下是几种调度task的方法:
//time为Date类型:在指定时间执行一次。
timer.schedule(task, time);
//firstTime为Date类型,period为long,表示从firstTime时刻开始,每隔period毫秒执行一次。
timer.schedule(task, firstTime, period);
//delay 为long类型:从现在起过delay毫秒执行一次。
timer.schedule(task, delay);
//delay为long,period为long:从现在起过delay毫秒以后,每隔period毫秒执行一次。
timer.schedule(task, delay, period);
//该任务每隔2秒更新主线程的UI(在主线程的TextView显示最新的系统时间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();
// 参数:
// 1000,延时1秒后执行。
// 2000,每隔2秒执行1次task。
timer.schedule(task, 1000, 2000);
}
@Override
protected void onStop() {
super.onStop();
// 暂停
// timer.cancel();
// task.cancel();
}
}
时间表有方法三个参数
第一个参数就是TimerTask的类型的对象,我们实现的TimerTask的run()的方法就是要周期执行的一个任务;
第二个参数有两种类型,第一种是长类型,表示多长时间后开始执行,另一种是日期类型,表示从那个时间后开始执行;
第三个参数就是执行的周期,为长类型。
调度方法还有一种两个参数的执行重载,第一个参数仍然是TimerTask的,第二个表示为长的形式表示多长时间后执行一次,为日期就表示某个时间后执行一次。
定时器就是一个线程,使用日程方法完成对TimerTask的的调度,多个TimerTask的可以共用一个定时器,也就是说定时器对象调用一次调度方法就是创建了一个线程,并且调用一次调度后的TimerTask是无限制的循环下去的,使用定时器的取消()停止操作。当然同一个定时器执行一次取消()方法后,所有定时器线程都被终止。
若要在TimerTask的中更新主线程UI,鉴于Android的编程模型不允许在非主线程中更新主线程UI,因此需要结合安卓的处理程序实现在Java的TimerTask的的中更新主线程UI。
public static ExecutorService newScheduledThreadPool(int corePoolSize){ return new ThreadPoolExecutor(corePoolSize,Integer.MAX_VALUE,0L,TimeUnit.SECONDS,new DelayedWorkQueue<Runnable>()); }
的ScheduledThreadPoolExecutor核心线程数量固定,非核心线程数没有限制。主要用于执行定时任务和具有固定周期的重复任务。
参考Java并发专题:计时器的缺陷使用ScheduledExecutorService替代
以前在项目中也经常使用定时器,比如每隔一段时间清理项目中的一些垃圾文件,每个一段时间进行数据清洗;然而计时器是存在一些缺陷的,因为定时器在执行定时任务时只会创建一个线程,所以如果存在多个任务,且任务时间过长,超过了两个任务的间隔时间,会发生一些缺陷。
1.定义了两个任务,预计是第一个任务1S后执行,第二个任务3S后执行
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);
}
}
运行结果:
task1 invoked ! 1000
task2 invoked ! 4000
TASK2实际上是4后才执行,正因为定时器内部是一个线程,而任务1所需的时间超过了两个任务间的间隔导致下面使用ScheduledThreadPool解决这个问题:
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)
{
/**
* 使用工厂方法初始化一个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,计时器会停止所有任务的运行:
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);
}
}
上面有两个任务,任务1抛出一个运行时的异常,任务2周期性的执行某个操作,输出结果:
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)
由于任务1的一次,任务2也停止运行了......下面使用ScheduledExecutorService的解决这个问题:
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出现异常时,不影响TASK2的运行:
task2 invoked!
task2 invoked!
task2 invoked!
task2 invoked!
task2 invoked!
YY才能执行,但由于CPU休眠造成线程阻塞的关系,当前系统时间超过了这个时间,即便CPU从终端中恢复了,那么由于条件不满足,定时任务在这一次自然就失效了。 解决方案是:它需要用激活锁定让CPU保持唤醒状态。那么问题就来了,这样会大量消耗手机电量(CPU唤醒和屏幕唤醒不是同一概念),大大减短手机待机时间。这种方式不能满足我们的需求。注:TimerTask的实现了Runnable接口,但它的运行方法只是简单的在定时器中封装的线程中被调用,并未将其放在其它线程中执行也就是说计时器是单线程执行的,那么问题来了。为何要这么费劲的封装一个Runnable接口又不进行多线程调用?我的答案是,老外只是想要表示它是可执行的方法。关于休眠,可以参考Android关于休眠引发的几个血案Android手机休眠后时间不准确的解决方案 AlarmManager是安卓系统封装的用于管理RTC 模块,RTC(实时时钟)是一个独立的硬件时钟,可以在CPU休眠时正常运行,在预设的时间到达时,通过中断唤醒CPU。这意味着,如果我们用AlarmManager来定时执行任务,CPU可以正常的休眠,只有在需要运行任务时醒来一段很短的时间。
以下参考Android基础入门教程--10.5 AlarmManager(闹钟服务)
核心流程如下:
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent(MainActivity.this, ClockActivity.class);
PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);
alarmManager.set(AlarmManager.RTC_WAKEUP,c.getTimeInMillis(), pi);
//10秒钟后执行一个任务
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);
相关方法:
关键参数讲解:
如果这三种方法错用了的话,虽然不会报错,但是看不到闹钟提示效果。
下面是一个每隔一小时就会在后台执行定时任务的服务。
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);
}
}
参考
Android实现倒计时之使用CountDownTimer
[Android] CountDownTimer简单用法与源码解析
在开发中会经常用到倒计时这个功能,包括给手机发送验证码等等,之前我的做法都是使用Handler + Timer + TimerTask来实现,现在发现了这个类,果断抛弃之前的做法,相信还是有很多人和我一样一开始不知道的Android已经帮我们封装好了一个叫CountDownTimer的类。
CountDownTimer timer = new CountDownTimer(10000,1000);
以毫秒为单位,第一个参数是指从开始调用启动()方法到倒计时完成的时候onFinish()方法被调用这段时间的毫秒数,也就是倒计时总时间;第二个参数表示间隔多少毫秒调用一次onTick方法,例如间隔1000毫秒。在调用的时候直接使用timer.start();
//共有4个方法,前两个抽象方法需要重写
public abstract void onTick(long millisUntilFinished);//固定间隔调用
public abstract void onFinish();//倒计时完成时被调用
public synchronized final void cancel();//取消倒计时,当再次启动会重新开始倒计时
public synchronized final CountDownTimer start();//启动倒计时
//该类的成员变量与成员函数均较少,功能实现的关键部分在于 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);
}
}
}
};
看一个使用例子:
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);
}
/**
* 取消倒计时
* @param v
*/
public void oncancel(View v) {
timer.cancel();
}
/**
* 开始倒计时
* @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) + "秒后可重发");
}
@Override
public void onFinish() {
mTvShow.setEnabled(true);
mTvShow.setText("获取验证码");
}
};
}
该方法就是利用我们常说的消息处理器。该方法原理就在主线程中创建一个Handler消息处理器,然后利用其中的一个postDelayed(Runnable r,long delayMillis)方法,该方法第一个参数需要传入一个可运行接口,并实现的run()方法,第二个参数就是延迟多少时间将run()的方法中的代码通过一个消息发送给消息队列,然后在主线程中执行这个消息中的代码,即是运行方法中的代码,从而实现在主线程中更新界面UI。
贴代码吧:
new Handler().postDelayed(new Runnable() {
//在当前线程(也即主线程中)开启一个消息处理器,并在3秒后在主线程中执行,从而来更新UI
@Override
public void run() {
//有关更新UI的代码
}
}, 3000);//3秒后发送
1.下载jdk新建文件夹java命令mkdir /home/java下载命令:wget --no-check-certificate -c --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u112-b15/jdk-8u1
网站的可用性(Avaliability)描述网站可有效访问的特性。1、网站可用性的度量与考核
FPGA、DPP和DSP知识简介FPGADPP(Digital Pulse Processing)DSP(Digital Signal Processing)FPGAFPGA 百度百科FPGA 维基百科DPP(Digital Pulse Processing)DPP-PHA(Digital Pulse Processing for the Pulse Height Analysis)...
1 #-*- coding:utf-8 -*-2 __author__ = 'Administrator'34 importtime5 importjson6 importre7 importrequests8 importexecjs9 importbase6410 from urllib.parse importurlencode11 from requests_toolbelt import...
virtualbox:VirtualBox-5.1.20-114628-OSX.dmg ubuntu: ubuntu-16.04.2-desktop-amd64.iso // 桌面版可以容易的实现复制粘贴 1. 网络设置: virtualbox的network里添加一个host o...
下载Modis数据
WeX5通过Baas服务访问数据库WeX5.Baas服务详解 视频地址:http://pan.baidu.com/s/1o6upyJ4baas.net 版:http://wex5.com/cn/wex5-baas-guide-net/1 概述本案例实现了对takeout_order(外卖订单表)表的分页查询、关键字检索和修改保存的能力,数据库表结构参见WeX5自带的...
解决办法安装插件 pipeline报错详情Started by user adminRunning in Durability level: MAX_SURVIVABILITY[Pipeline] Start of Pipeline[Pipeline] End of Pipelinejava.lang.NoSuchMethodError: No such DSL method ...
一、ISO目录文件介绍:1、ISO目录列表:[[email protected] centos6]# lltotal 552-r--r--r--. 1 root root 14 Nov 29 19:52 CentOS_BuildTag-r--r--r--. 1 root root 31 Nov 29 20:08 .discinfo
能写SQL是程序员的基本功,而能写出性能优异的SQL是优秀程序员的必备技能。 可那些性能好的SQL语句是怎么写出来的?难道他们了解数据库底层的东西吗? 其实了解数据库原理是一方面,更快捷的是借助“执行计划”(Explain Plan)来分析SQL语句执行的步骤及过程。不同的数据库...
MegaCli查看RAID级别:## 查raid卡信息(生产商、电池信息及所支持的raid级别)/usr/local/sbin/MegaCli -AdpAllInfo -aALL |grep -E "Product Name|BBU|Memory Size|RAID Level Supported"## 查看RAID卡逻辑盘信息/usr/local/sbin/MegaCli -LDInfo -L...
不多说直接上代码复制可用有个问题,就是当你引包的时候这三个会报错,解决方案:右键项目properties->java build path->libraries,将JRE删除,重新导入就可以了。 给个关注吧(- . -).import com.sun.image.codec.jpeg.JPEGCodec;import com.sun.image.codec.jpeg.JPEGE...