前言
本次博客主要针对 定时任务 这一块做了一下梳理其用法。
Android 中的定时任务一般有两种实现方式,一种是Java API 里提供的 Timer 类,
一种是使用 Android 的 Alarm 机制。这两种方式在多数情况下都能实现类似的效果,但 Timer
它并不太适用于那些需要长期在后台运行的定时任务。为
了能让电池更加耐用,每种手机都会有自己的休眠策略,Android 手机就会在长时间不操作。
的情况下自动让 CPU 进入到睡眠状态,这就有可能导致 Timer 中的定时任务无法正常运行。
而 Alarm 机制则不存在这种情况,它具有唤醒 CPU 的功能,即可以保证每次需要执行定时
任务的时候 CPU 都能正常工作
Timer用法
Timer
和TimerTask
Timer
是java中定时器工具,使用的时候会在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次。
TimerTask
是一个实现了Runnable
接口的抽象类,可以被Timer
执行的任务。
直接上Demo
public class MainActivity extends AppCompatActivity {
private TimerTask mTimerTask;
private Timer mTimer;
private TextView mTv;
private int count = 0;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int count = msg.arg1;
mTv.setText(count + "");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = findViewById(R.id.tv);
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
Message message = Message.obtain();
message.arg1 = count;
count++;
mHandler.sendMessage(message);
}
};
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTimer.schedule(mTimerTask, 1000,1000);
}
});
}
}
程序执行结果为每隔一秒TextView
的数值加一,通过Handler
来实现线程间通信,这里不再详述Handler
。
首先我们通过new
关键字可以得到Timer
对象和TimerTask
对象并重写TimerTask
中 run
方法,里面可以做一些你想做的逻辑操作,然后通过Handler
发送消息,重写 handleMessage(Message msg)
在这里做一些更新UI的操作。
当我们需要结合服务一起使用时,我们都知道服务里是不可以做耗时操作的,Service耗时操作为20秒,我们可以使用IntentService,或者通过广播结合服务一起使用,这里就不再详述。
Alarm 机制的用法
通过调用 Context
的
getSystemService()
方法来获取实例的,只是这里需要传入的参数是 Context.ALARM_SERVICE
。
获取一个 AlarmManager
的实例:
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
调用 AlarmManager 的 set()方法设置一个定时任务,例如设定一
个任务在 5 秒钟后执行的方法:
long delayTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
第一个参数是一个整型参数,用于指定 AlarmManager的
工作类型,有四种值可选,分别是
ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、RTC 和RTC_WAKEUP。
ELAPSED_REALTIME
表示让定时任务的触发时间从系统开
机开始算起,但不会唤醒 CPU。
ELAPSED_REALTIME_WAKEUP
同样表示让定时任务的触
发时间从系统开机开始算起,但会唤醒 CPU。
RTC
表示让定时任务的触发时间从 1970 年 1
月 1 日 0 点开始算起,但不会唤醒 CPU。
RTC_WAKEUP
同样表示让定时任务的触发时间从
1970 年 1 月 1 日 0 点开始算起,但会唤醒 CPU。
SystemClock.elapsedRealtime()
方法可
以获取到系统开机至今所经历时间的毫秒数。
System.currentTimeMillis()
方法可以获取
到 1970 年 1 月 1 日 0 点至今所经历时间的毫秒数。
第二个参数,定时任务触发的时间,以毫秒为单位。如果第一个参数使用的是 ELAPSED_REALTIME或 ELAPSED_REALTIME_WAKEUP
,
则这里传入开机至今的时间再加上延迟执行的时间。如果第一个参数使用的是 RTC
或
RTC_WAKEUP
,则这里传入 1970 年 1 月 1 日 0 点至今的时间再加上延迟执行的时间。
第三个参数是PendingIntent
,调
用 getBroadcast
()方法来获取一个能够执行广播的 PendingIntent
。当定时任务被触发的时候,广播接收器的 onReceive
()方法就可以得到执行。
长期在后台执行定时任务的服务。创建一个 ServiceBestPractice 项目,然后新增一个 LongService
类,代码如下所示:
public class LongService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
Log.i("LongService", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "执行");
}
}).start();
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 5 * 1000;
Intent intent1 = new Intent(this, LongBroadcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent1, PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
return super.onStartCommand(intent1, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
新建一个 LongBroadcastReceiver
类,并让它继承自 BroadcastReceiver
,代码如下所示:
public class LongBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent intent1 = new Intent(context,LongService.class);
context.startService(intent1);
}
}
onReceive
()方法里构建出了一个 Intent 对象,然后去启动
LongService
这个服务。
MainActivity
中的代码,如下所示:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this,LongService.class);
startService(intent);
}
}
最后服务和广播接收器都要在 AndroidManifest
.xml 中注册,
`<application`
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".LongService"></service>
<receiver android:name=".LongBroadcastReceiver"></receiver>
</application>
然后观察 LogCat打印日志。
Android 4.4 版本开始,Alarm 任务的触发时间将会变得不准确,
有可能会延迟一段时间后任务才能得到执行。这并不是个 bug,而是系统在耗电性方面进行
的优化。系统会自动检测目前有多少 Alarm 任务存在,然后将触发时间将近的几个任务放在
一起执行,这就可以大幅度地减少 CPU 被唤醒的次数,从而有效延长电池的使用时间。
当然,如果你要求 Alarm 任务的执行时间必须准备无误,Android 仍然提供了解决方案。
使用 AlarmManager 的 setExact()方法来替代 set()方法,就可以保证任务准时执行了。
Alarm参考第一行代码
菜鸟学习中…有错望指正!谢谢