Android 定時任務之Service + AlarmManger + BroadcastReceiver

AlarmManger

鬧鐘服務,在特定的時刻爲我們廣播一個指定的Intent,簡單說就是我們自己定一個時間, 然後當到時間時,AlarmManager會爲我們廣播一個我們設定好的Intent,比如時間到了,可以指向某個Activity或者Service。另外要注意一點的是,AlarmManager主要是用來在某個時刻運行你的代碼的,即時你的APP在那個特定時間並沒有運行!還有,從API 19開始,Alarm的機制都是非準確傳遞的,操作系統將會轉換鬧鐘 ,來最小化喚醒和電池的使用!某些新的API會支持嚴格準確的傳遞,見 setWindow(int, long, long, PendingIntent)和setExact(int, long, PendingIntent)。 targetSdkVersion在API 19之前應用仍將繼續使用以前的行爲,所有的鬧鐘在要求準確傳遞的情況下都會準確傳遞。
在這裏插入圖片描述
更多詳情可見官方API文檔:AlarmManager

設置鬧鐘

  • set(int type,long startTime,PendingIntent pi):一次性鬧鐘
  • setRepeating(int type,long startTime,long intervalTime,PendingIntent pi): 重複性鬧鐘,時間不固定
  • setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi): 重複性鬧鐘,時間不固定
  • setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation): 和set方法類似,這個鬧鐘運行在系統處於低電模式時有效
  • setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation):和setAndAllowWhileIdle一樣,如果需要在低功耗模式下觸發鬧鐘,那可以使用這個方法。
  • setExact(int type, long triggerAtMillis, PendingIntent operation): 在規定的時間精確的執行鬧鐘,比set方法設置的精度更高,建議使用這個方法設置鬧鐘

setInexactRepeating和setRepeating兩種方法都是用來設置重複鬧鈴的,setRepeating執行時間更爲精準。在Android 4.4之後,Android系統爲了省電把時間相近的鬧鈴打包到一起進行批量處理,這就使得setRepeating方法設置的鬧鈴不能被精確的執行,必須要使用setExact來代替。但是手機進入低電耗模式之後,這個時候通過setExact設置的鬧鈴也不是100%準確了,需要用setExactAndAllowWhileIdle方法來設置,鬧鈴才能在低電耗模式下被執行。
在這裏插入圖片描述
更多詳情可見官方API文檔:針對低電耗模式和應用待機模式進行優化

上面方法中的參數說明:

  • type:鬧鐘類型,有五種類型:

 AlarmManager.ELAPSED_REALTIME: 鬧鐘在手機睡眠狀態下不可用,該狀態下鬧鐘使用相對時間(相對於系統啓動開始),狀態值爲3
 AlarmManager.ELAPSED_REALTIME_WAKEUP:鬧鐘在睡眠狀態下會喚醒系統並執行提示功能,該狀態下鬧鐘也使用相對時間,狀態值爲2
 AlarmManager.RTC:鬧鐘在睡眠狀態下不可用,該狀態下鬧鐘使用絕對時間,即當前系統時間,狀態值爲1
 AlarmManager.RTC_WAKEUP:表示鬧鐘在睡眠狀態下會喚醒系統並執行提示功能,該狀態下鬧鐘使用絕對時間,狀態值爲0
 AlarmManager.POWER_OFF_WAKEUP:表示鬧鐘在手機關機狀態下也能正常進行提示功能,所以是5個狀態中用的最多的狀態之一,該狀態下鬧鐘也是用絕對時間,狀態值爲4;不過本狀態好像受SDK版本影響,某些版本並不支持;

  • startTime:鬧鐘的第一次執行時間,以毫秒爲單位,可以自定義時間,不過一般使用當前時間。 需要注意的是:本屬性與第一個屬性(type)密切相關,
    如果第一個參數對應的鬧鐘使用的是相對時間 (ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那麼本屬性就得使用相對時間 (相對於系統啓動時間來說),比如當前時間就表示爲:SystemClock.elapsedRealtime();
    如果第一個參數對應的鬧鐘使用的是絕對時間(RTC、RTC_WAKEUP、POWER_OFF_WAKEUP), 那麼本屬性就得使用絕對時間,比如當前時間就表示爲:System.currentTimeMillis()。

  • intervalTime:表示兩次鬧鐘執行的間隔時間,也是以毫秒爲單位。

  • PendingIntent:綁定了鬧鐘的執行動作,比如發送一個廣播、給出提示等等。不過需要注意:
    如果是通過啓動服務來實現鬧鐘提示的話,PendingIntent對象的獲取就應該採用Pending.getService (Context c,int i,Intent intent,int j)方法;
    如果是通過廣播來實現鬧鐘提示的話,PendingIntent對象的獲取就應該採用PendingIntent.getBroadcast (Context c,int i,Intent intent,int j)方法;
    如果是採用Activity的方式來實現鬧鐘提示的話,PendingIntent對象的獲取就應該採用 PendingIntent.getActivity(Context c,int i,Intent intent,int j)方法。
    如果這三種方法錯用了的話,雖然不會報錯,但是看不到鬧鐘提示效果。

  • triggerAtMillis:鬧鐘觸發時間,與startTime一樣,也是以毫秒爲單位,跟第一個屬性(type)密切相關。

取消鬧鐘

//如果是PendingIntent方式註冊的鬧鈴。
cancel(PendingIntent pi)
//如果是AlarmManager.OnAlarmListener方式註冊的鬧鈴
cancel(AlarmManager.OnAlarmListener listener)

Service + AlarmManger + BroadcastReceiver

上面方式結合可以實現精確定時,適用於配合service在後臺執行一些長期的定時行爲,比如每隔五分鐘保存一次歷史數據。

獲取AlarmManager實例對象:

AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

設置鬧鐘:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0低電耗模式需要使用此方法才能準時觸發定時任務
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4以上,使用此方法觸發定時任務時間更爲精準
            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
        } else {//4.4以下,使用舊方法
            alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent);
        }

貼上全部代碼:

public class HistoryDataService extends Service {

    private static final String ALARM_ACTION = "SAVE_HISTORY_DATA_ACTION";
    private static final int TIME_INTERVAL = 300000; // 5min 1000 * 60 * 5
    private PendingIntent pendingIntent;
    private AlarmManager alarmManager;

    private BroadcastReceiver alarmReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //定時任務
            //todo
            Log.i("HistoryDataService", "onReceive: 執行保存歷史數據");

            //如果版本高於4.4,需要重新設置鬧鐘
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
            }
        }
    };

    public HistoryDataService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();

        IntentFilter intentFilter = new IntentFilter(ALARM_ACTION);
        registerReceiver(alarmReceiver, intentFilter);

        alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ALARM_ACTION), 0);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0低電耗模式需要使用此方法才能準時觸發定時任務
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4以上,使用此方法觸發定時任務時間更爲精準
            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
        } else {//4.4以下,使用舊方法
            alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent);
        }
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(alarmReceiver);
        alarmManager.cancel(pendingIntent);
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

參考文章:AlarmManager(鬧鐘服務)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章