您的位置:首页 > 移动开发 > Android开发

android Service重启问题,结合AlarmManager实现定时任务

2015-04-28 13:29 1766 查看

android Service重启问题,结合AlarmManager实现定时任务

标签:
网络轮询服务重启service重启alarmmanager定时任务

2015-04-28 13:29
2430人阅读 评论(0)
收藏
举报

本文章已收录于:


分类:
Android(6)




作者同类文章X

版权声明:本文为博主原创文章,未经博主允许不得转载。


        当启动service进行后台任务的时候,我们一般的 做法是启动一个线程,然后通过sleep方法来控制进行定时的任务,如轮询操作,消息推送。这种service的资源是很容易被回收的,虽然service的优先级很高,但是还没有前台的activity的优先极高,所以一旦资源被回收,service会停止运行。
        service被回收是我们不能控制的,但是我们可以控制service的重启活动。在service的onStartCommand
方法中可以返回一个参数来控制重启活动
        1、Service.START_STICKY,就会在资源被回收(用手机加速程序加速)或者程序异常(异常结束程序,测试的时候手动抛出一个异常)的时候重新调用oncreate、onStartCommand来启动服务。但是不能连续重启两次。像音乐播放器,这种需要持续运行的,断开后可以马上重启,但是传送过来的intent为null。
            这种模式通常用于处理自身状态的service,以及需要通过startService和stopService显示的启动和终止的Service,如音乐播放器的service
        2、START_NOT_STICKY就不会重新启动服务。比如说更新操作,他会考虑到资源竞争,比较谨慎,不会自动启动service。此处onstartcommand方法中的intent是本次传送过来的intent。当被异常终止后,只有重新startService 调用,这个servie才会启动。
            这种模式比较谨慎,当停止后,它会在下一个调度中尝试重新启动,不会再资源竞争的时候重启,对于处理特殊请求,尤其诸如更新操作或者网络轮询这样的定期处理。
        3、START_REDELIVER_INTENT不会马上重启service。需要在下次调用启动这个service的时候会获得上次传入的intent,然后执行两次onStartCommand方法。只执行一次oncreat方法。也就是说可以把上次没有完成的工作这一次也完成(传送过来的intent是保留的上一次的intent和本次传送的intent)。一般用于不是无限循环的任务。
        这种模式是要确保service中的命令得以完成。--例如在时效性比较重要的时候
注意,在处理完成后,每种模式都要求使用stopService和stopSelf显式的停止service。

从以上的参数中可以看出,service的重启操作不能只依靠参数返回值来实现,能够重启的只有Service.START_STICKY,但是这种方法只能重启一次,再次断开的时候就不能实现重连操作了。所以必须使用AlarmManager的定时任务来实现网络的轮询等定时任务。可以看到使用START_NOT_STICKY的返回值是最适合网络轮询操作的。

AlarmManager的工作原理。alarmmanager会定时的发出一条广播,然后在自己的项目里面注册这个广播,重写onReceive方法,在这个方法里面启动一个service,然后在service里面进行网络的访问操作,当获取到新消息的时候进行推送,同时再设置一个alarmmanager进行下一次的轮询,当本次轮询结束的时候可以stopself结束改service。这样即使这一次的轮询失败了,也不会影响到下一次的轮询。这样就能保证推送任务不会中断。

[java]
view plain
copy

print?

@Override  
public int onStartCommand(Intent intent, int flags, int startId) {  
new Thread(new Runnable() {  
@Override  
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);  
}  
}  



@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
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);
}
}


 我们在
onStartCommand()方法里开启了一个子线程, 然后在子线程里就可以执行具体的逻辑操作了。这里简单起见,只是打印了一下当前的时间。
创建线程之后的代码就是我们刚刚讲解的
Alarm 机制的用法了,先是获取到了AlarmManager 的实例,然后定义任务的触发时间为一小时后,再使用 PendingIntent 指定处理定时任务的广播接收器为 AlarmReceiver,最后调用
set()方法完成设定。
显然,AlarmReceiver目前还不存在呢,所以下一步就是要新建一个
AlarmReceiver类,
并让它继承自
BroadcastReceiver,代码如下所示: 我们在 onStartCommand()方法里开启了一个子线程, 然后在子线程里就可以执行具体的逻辑操作了。这里简单起见,只是打印了一下当前的时间。

创建线程之后的代码就是我们刚刚讲解的 Alarm 机制的用法了,先是获取到了AlarmManager 的实例,然后定义任务的触发时间为一小时后,再使用 PendingIntent 指定处理定时任务的广播接收器为 AlarmReceiver,最后调用 set()方法完成设定。

显然,AlarmReceiver目前还不存在呢,所以下一步就是要新建一个 AlarmReceiver类,

并让它继承自 BroadcastReceiver,代码如下所示:

[html]
view plain
copy

print?

public class AlarmReceiver extends BroadcastReceiver {  
@Override  
public void onReceive(Context context, Intent intent) {  
Intent i = new Intent(context, LongRunningService.class);  
context.startService(i);  
}  
}  



public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, LongRunningService.class);
context.startService(i);
}
}


onReceive()方法里的代码非常简单,就是构建出了一个
Intent 对象,然后去启动LongRunningService
这个服务。那么这里为什么要这样写呢?其实在不知不觉中,这就已经将一个长期在后台定时运行的服务完成了。因为一旦启动
LongRunningService,就会在onStartCommand()方法里设定一个定时任务,这样一小时后
AlarmReceiver 的 onReceive()方法就将得到执行,然后我们在这里再次启动
LongRunningService,这样就形成了一个永久的循环,保证
LongRunningService 可以每隔一小时就会启动一次,一个长期在后台定时运行的服务自然也就完成了。
 
      另外需要注意的是,从 Android 4.4 版本开始,Alarm 任务的触发时间将会变得不准确,有可能会延迟一段时间后任务才能得到执行。这并不是个
bug,而是系统在耗电性方面进行的优化。系统会自动检测目前有多少
Alarm任务存在,然后将触发时间将近的几个任务放在一起执行,这就可以大幅度地减少
CPU被唤醒的次数,从而有效延长电池的使用时间。当然,如果你要求
Alarm任务的执行时间必须准备无误,Android仍然提供了解决方案。使用
AlarmManager的 setExact()方法来替代 set()方法,就可以保证任务准时执行了。



顶 0 踩 0
 
 
上一篇Activity的启动模式详解

下一篇程序退到后台在返回,application中的缓存数据被回收,导致程序异常

我的同类文章

Android(6)

http://blog.csdn.net
Drawable分类2016-01-03阅读181

android中webView和html中js的交互2015-05-05阅读342

程序退到后台在返回,application中的缓存数据被回收,导致程序异常2015-04-28阅读648

JNI和NDK2015-08-01阅读174

java堆和栈2015-05-04阅读225

Activity的启动模式详解2015-04-28阅读274
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 安卓