android Service重啓問題,結合AlarmManager實現定時任務


        當啓動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。這樣即使這一次的輪詢失敗了,也不會影響到下一次的輪詢。這樣就能保證推送任務不會中斷。
@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,代碼如下所示:
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()方法,就可以保證任務準時執行了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章