Android 中的定时任务

前言

  • 项目中有用到定时任务,就捎带着总结一下

  • 本篇文章,一部分来自于 《第一行代码第2版》

  • 本文着重说明下 Android 中的 Alarm 机制

正文

一、Android 中实现定时任务的几种方式

1. java 中的 Timer 类

分享一个 Timer 详解链接: http://blog.csdn.net/zuolongsnail/article/details/8168689

  • Timer 类适合短期在后台运行的定时任务,而不适合长期的,因为现在的手机都有自己的休眠策略,Android 手机会在长时间不操作的情况下会自动让 CPU 进入到休眠状态,这就有可能导致 Timer 中的定时任务无法正常运行。
2. Handler

分享一个链接: http://blog.csdn.net/dxpqxb/article/details/8659292

  • Handler post系列方法中有定时发送消息的操作,如:handler.postDelayed();
3. Android 中的 Alarm 机制
  • Alarm 机制具有唤醒 CPU 的功能,它能保证在多数情况下需要执行定时任务的时候 CPU 都能正常工作,下面会详细介绍

  • Alarm 需要在 API 19 及其以上才能使用。

  • 经测试,Alarm 有毫秒级别的误差

1
2
3
4
5
6
7
8
9
10
11
12
13
04-17 15:12:41.208 5615-5632/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:12:51.218 5615-5677/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:13:01.223 5615-5757/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:13:11.228 5615-5926/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:13:21.233 5615-6098/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:13:31.238 5615-6266/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:13:41.243 5615-6436/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:13:51.248 5615-6610/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:14:01.258 5615-6780/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:14:11.258 5615-6951/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:14:21.263 5615-7122/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:14:31.268 5615-7291/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------
04-17 15:14:41.273 5615-7466/io.github.adsuper.timed_task I/Alarm: -+------------定时任务--------------

二、 Alarm 机制

1. 核心类: AlarmManager
1
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
2. 调用 AlarmManager 的 set() 系列方法就可以设置一个定时任务
1
2
3
4
5
6
long time = SystemClock.elapsedRealtime() + 10 * 1000;
Intent intent1 = new Intent(this , LongRunningService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent1, 0);
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP , time , pendingIntent);
set 系列方法三个参数详解
  1. 第一个参数有四个选项分别为:
1
2
3
4
// AlarmManager.ELAPSED_REALTIME ,表示让定时任务的触发事件从系统开机开始算起,但是不会唤醒 CPU
// AlarmManager.ELAPSED_REALTIME_WAKEUP ,表示让定时任务的触发事件从系统开机开始算起,但是会唤醒 CPU
// AlarmManager.RTC ,表示让定时任务的触发事件从 1970 年 1 月 1 日 0 点开始算起,但不会唤醒 CPU
// AlarmManager.RTC_WAKEUP ,表示让定时任务的触发事件从 1970 年 1 月 1 日 0 点开始算起,但会唤醒 CPU
  1. 第二个参数表示定时任务的触发时间:
  • 如果第一个参数使用的是 ELAPSED_REALTIME 或者 ELAPSED_REALTIME_WAKEUP ,则这里就传入开机时间加上延迟执行的时间。

  • 获取开机至今所经历的时间的毫秒数方法为:SystemClock.elapsedRealtime();

  • 如果第一个参数使用的是 RTC 或者 RTC_WAKEUP,则这里就传入从 1970 年 1 月 1 日 0 点至今所经历时间的毫秒数加上延迟执行的时间。

  • 获取从 1970 年 1 月 1 日 0 点至今所经历的时间的毫秒数的方法为:System.currentTimeMillis();

  1. 第三个参数是一个 PendingIntent ,表示一个后台可以长时间运行的服务
set() 和 setExact() 的区别
  • 从 Android 4.4 系统开始,Alarm 任务的触发事件将会变得不准确,有可能会延迟一段时间后任务才能执行,但这并不是 bug,而是系统在耗电方面进行的优化。系统会自动监测目前有多少个 Alarm 任务存在,然后触发时间相近的几个任务会放在一起执行,这就可以大幅度减少 CPU 被唤醒的次数,从而有效延长电池的使用时间。

  • 如果要求 Alarm 任务的执行时间必须准确无误,Android 仍然提供了解决方案,使用 setExact() 方法来代替 set() 方法。

3. 完整的代码
  • Activity 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package io.github.adsuper.timed_task;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.util.Timer;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//开启一个服务
Intent intent = new Intent(this,LongRunningService.class);
startService(intent);
}
}
  • Service 中的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package io.github.adsuper.timed_task;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.util.Log;
public class LongRunningService extends Service {
private AlarmManager alarmManager;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//执行具体的定时逻辑操作
//逻辑操作也是需要耗时的,如果放在主线程中,可能会对定时任务的准确性造成轻微的影响
Log.i("Alarm","-+------------定时任务--------------");
}
}).start();
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long time = SystemClock.elapsedRealtime() + 10 * 1000;
Intent intent1 = new Intent(this , LongRunningService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent1, 0);
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP , time , pendingIntent);
return super.onStartCommand(intent, flags, startId);
}
}
4. 另外需要注意的事项
  • 取消定时任务
1
2
//pendingIntent 要跟设置的时候一致才能取消
alarmManager.cancel(pendingIntent);
  • Alarm 也能定时发送广播
1
2
// 参数与 set() 类似
alarmManager.setRepeating();
  • 还有一系列其他的操作,有待发掘