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常量。
Android Service,AlarmManager组合实现定时任务踩的坑_第1张图片

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

当改为 FLAG_UPDATE_CURRENT 时,每次发送的广播传递的Intent就能更新了。
Android Service,AlarmManager组合实现定时任务踩的坑_第2张图片

将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经过多方面测试才能完善。

你可能感兴趣的:(安卓开发)