1. 程式人生 > >Android Service,AlarmManager組合實現定時任務踩的坑

Android Service,AlarmManager組合實現定時任務踩的坑

做專案時遇到一個場景:app需要定時訪問後臺,感知獲取登入使用者有沒有最新的訊息。

我採用了定義一個Service,在onStartCommand()方法中請求後臺獲取最新訊息,接著建立一個AlarmManager來延時傳送廣播,再定義一個廣播接收器,接收到一個廣播後,接收器觸發startService( ),這時service的onStartCommand再次被觸發,就再次請求獲取最新訊息,繼續傳送廣播,如此往復。

Created with Raphaël 2.1.0MyNewTaskServiceMyNewTaskServiceMyNewTaskReceiverMyNewTaskReceiver
AlarmManager(5s)|param:Intent->userIdintent.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經過多方面測試才能完善。