需求:需要app在后台一直记录当前位置坐标,5s上传服务器一次,app退出依然执行。
分析:根据需求,决定使用AlarmManager+pendingIntent启动一个service,后台记录当前坐标。
AlarmManager:
AlarmManager是一个全局的定时器,是Android中常用的一种系统级别的提示服务,在指定时间或周期性启动其它组件(包括Activity,Service,BroadcastReceiver)。
(1)set(int type,long startTime,PendingIntent pi);
该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。
(2)setRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
该方法用于设置重复闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,第三个参数表示闹钟两次执行的间隔时间,第三个参数表示闹钟响应动作。
(3)setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
该方法也用于设置重复闹钟,与第二个方法相似,不过其两个闹钟执行的间隔时间不是固定的而已。间不是固定的而已。
(4)setWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation)
该方法是针对API>=19使用,因为API>19之后,android平台为了省电机制,setRepeating()已经不再准确
参数和setRepeating的参数大致类似。
type类型:
闹钟的类型,常用的有5个值:AlarmManager.ELAPSED_REALTIME、 AlarmManager.ELAPSED_REALTIME_WAKEUP、AlarmManager.RTC、 AlarmManager.RTC_WAKEUP、AlarmManager.POWER_OFF_WAKEUP。
AlarmManager.ELAPSED_REALTIME表示闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3;
AlarmManager.ELAPSED_REALTIME_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2;
AlarmManager.RTC表示闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1;
AlarmManager.RTC_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0;
AlarmManager.POWER_OFF_WAKEUP表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持;
long startTime:
闹钟的第一次执行时间,以毫秒为单位,可以自定义时间,不过一般使用当前时间。需要注意的是,本属性与第一个属性(type)密切相关。
如果第一个参数对应的闹钟使用的是相对时间(ELAPSED_REALTIME,ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间(相对于 系统启动时间来说),比如当前时间就表示为:SystemClock.elapsedRealtime();
如果第一个参数对应的闹钟使用的是绝对时间 (RTC、RTC_WAKEUP、POWER_OFF_WAKEUP),那么本属性就得使用绝对时间,比如当前时间就表示 为:System.currentTimeMillis()。
long intervalTime:
对于(2),(3),(4)方法来说,存在本属性,表示两次闹钟执行的间隔时间,也是以毫秒为单位。
PendingIntent pi:
需要一个pendingIntent对象,一般有三种形势,activity,broadcastReceiver,service
启动一个service:
public static void setAlarmServiceTime(Context context, long timeInMillis, int time) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, LocationService.class);
PendingIntent sender = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
int interval = time;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.v("wsh", "api大于19");
//参数2是开始时间、参数3是允许系统延迟的时间
am.setWindow(AlarmManager.RTC_WAKEUP, timeInMillis, interval, sender);
} else {
Log.v("wsh", "api小于19");
am.setRepeating(AlarmManager.RTC_WAKEUP, timeInMillis, interval,sender;
}
}
调用:AlarmUtils.setAlarmServiceTime(this, SystemClock.elapsedRealtime(), 5 * 1000);), 5 * 1000);
在service中为了防止API>19不周期性执行,所以重新调用:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//因为setWindow只执行一次,所以要重新定义闹钟实现循环。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.v("wsh", "onStartCommand KITKAT " + flags);
AlarmUtils.setAlarmServiceTime(this, SystemClock.elapsedRealtime(), 3 * 1000);
}
Log.v("wsh", "onStartCommand" + flags);
return Service.START_STICKY;
}
启动一个broadcastReceiver:
public static void setAlarmReceiverTime(Context context, long timeInMillis, int time) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingReceiver pendingReceiver = new PendingReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.pending");
context.registerReceiver(pendingReceiver,filter);
Intent intent = new Intent();
intent.setAction("com.pending");
intent.putExtra("pending","我是setAlarmReceiverTime");
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
int interval = time;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.v("wsh", "api大于19");
//参数2是开始时间、参数3是允许系统延迟的时间
am.setWindow(AlarmManager.RTC_WAKEUP, timeInMillis, interval, sender);
} else {
Log.v("wsh", "api小于19");
am.setRepeating(AlarmManager.RTC_WAKEUP, timeInMillis, interval, sender);
}
调用:AlarmUtils.setAlarmReceiverTime(this, SystemClock.elapsedRealtime(), 3 * 1000);
在API>19时,设置的周期time不管用,默认每5s执行一次,不知道是什么原因。