Android Service,AlarmManager组合实现定时任务踩的坑


做项目时遇到一个场景:app需要定时访问后台,感知获取登录用户有没有最新的消息。

我采用了定义一个Service,在onStartCommand()方法中请求后台获取最新消息,接着创建一个AlarmManager来延时发送广播,再定义一个广播接收器,接收到一个广播后,接收器触发startService( ),这时service的onStartCommand再次被触发,就再次请求获取最新消息,继续发送广播,如此往复。

Created with Raphaël 2.1.0 MyNewTaskService MyNewTaskService MyNewTaskReceiver MyNewTaskReceiver AlarmManager(5s)|param:Intent->userId intent.getStringExtra("userId") startService(Intent->userId) 通过userId访问网络

用户名userId是登录成功后就能得到的。在登录成功进入主界面后,并立即启动MyNewTaskService

MyNewTaskService的主要业务逻辑代码如下:

    public int onStartCommand(Intent intent, int flags, int startId) {
mRealm = Realm.getDefaultInstance();
mUserDao = new UserDao(mRealm);//获取当前登录用户id
mTaskTimeDao = new TaskTimeDao(mRealm);
if(intent!=null){
userId = intent.getStringExtra("userId");
}else{
userId = mUserDao.getUserInfo().getUserId();//realm也保存了用户信息,但不一定每次都能获取到
}
if(null!=userId){
//...做一些事情,比如访问网络获取最新消息
//设置定时操作
AlarmManager am= (AlarmManager)getSystemService(ALARM_SERVICE);
//1分钟请求一次更新
int elapseTime = 1*60*1000;
long interval = SystemClock.elapsedRealtime()+elapseTime;
//传递userId参数给MyNewTaskReceiver,为了到时候回传回来
Intent i = new Intent(this,MyNewTaskReceiver.class);
i.putExtra("userId",userId);
PendingIntent pi = PendingIntent.getBroadcast(this,0,i,0);
am.set(AlarmManager.ELAPSED_REALTIME,interval,pi);
}
return super.onStartCommand(intent, flags, startId);
}

下面是MyNewTaskReceiver类

public class MyNewTaskReceiver extends BroadcastReceiver{    
@Override
public void onReceive(Context context, Intent intent) {
Intent i= new Intent(context, MyNewTaskService.class);
i.putExtra("userId",intent.getStringExtra("userId"));
Log.i("user",intent.getStringExtra("userId"));
context.startService(i);
//每1分钟将接收到一次广播,这时触发service的onStartCommand()执行需要的重复操作
}
}

但是app测试时发现,当第一次登录进去后,例如此时用户名是 ‘zs’ ,这是后台可以每1分钟接收到’zs’的最新的消息。然而,现在’zs’退出登录,用’ls’账号登录,这时后台返回的消息仍然还是’zs’的,而不是’ls’的。

后来发现,每次第一个登录的用户登陆后,后面不管任何其他用户再次登录同一台手机,后台返回的都是第一个用户的信息。因为App设计的时第一次登录成功后,Service会一直在后台执行。所以怀疑第一次创建Service后,Service所持有的userId并没有更新。

经过调试,发现,MyNewTaskReceiver接收的Intent中获取的userId全都是第一次登录用户的id。猜想可能发送广播时传入的参数有问题。

经过查阅安卓API发现,定时器AlarmManager初始化时传入的PendingIntent的构造函数的第四个参数需要一个Flag常量。
这里写图片描述

之前我传入的是0。没有作用。

当改为 FLAG_UPDATE_CURRENT 时,每次发送的广播传递的Intent就能更新了。
这里写图片描述

将onStartCommand()方法的这一行改为

 PendingIntent pi = PendingIntent.getBroadcast(this,0,i,0);
PendingIntent pi = PendingIntent.getBroadcast(this,0,i,FLAG_UPDATE_CURRENT);

这时不管那个用户登录,后台都可以定时返回当前用户的消息了。

总结&注意:

1 该示例的后台Service在用户关闭app后并没有stop。如果stop,相信不会有这种Bug产生。因为定时任务是依附于Service存在的。

2 曾经尝试在主Activity的onDestory()中调用StopService()方法让app关闭后service也结束运行,没有成功。如果service在app关闭后也停止运行,相信不会有这种bug产生。

3 app经过多方面测试才能完善。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: