言歸正傳! 在最近的Bug中發現一個問題,項目有用到預定的功能,因此就使用了AlarmManager來進行定時提醒的功能。當時開發這個功能的時候並無發現會有這種Bug,緣由是開發時只會預定一個來測試功能是否有實現,實現了就算完成,而這個問題剛好是有多個預定時纔會發生的問題(這裏的多個預定是指系統中有多個AlarmManager的定時,具體爲何這樣說,下面我會提到),好比當我進行了10個預定,也就是設定了10個AlarmManager的時候,我讓設備休眠(我用的Type是RTC_WAKEUP),依次觀察10個通知的提醒。發現其中會有一兩個通知並無按照我設定的時間喚醒設備,而是會和與他時間相近的下一個預定同時出現(也就是發生了時間不精確的問題)。當時發現這個問題時覺得是我在傳遞預定時間時發生了問題,就打Log查看了一下,發現並無什麼問題,既然時間沒有問題,那頗有多是AlarmManager的問題,上網查了一下,在Android的文檔中發現了端倪。android
官方文檔說從API19(android4.4)開始, 爲了節能省電(減小系統喚醒和電池使用)。使用Alarm.set()和Alarm.setRepeating()已經不保證精確性(是否是忽然一下就蒙了,不精確還怎麼用?),不過Google怎麼可能會作出這樣不合理的設計呢!接着看就會發現其實Google還提供兩個精確的Alarm方法,setWindow()和setExact(),看來問題是解決了。那順便也分析一下AlarmManager吧,分析的過程當中發現使用setAlarmClock這個方法也能夠實現精準通知,可是有侷限性,下面會說到。測試
瀏覽了一下官方文檔和AlarmManager這個類,發現其實這個類很簡單,就是定義了幾個Type變量和set、cancle方法(其實AlarmManager的核心在AlarmManagerService中,這個一下子再說)。這裏只介紹一些常用的。設計
常用的變量:
其實主要就是分爲兩類,系統相對時間和絕對時間和是否喚醒CPU。
ELAPSED_REALTIME:使用相對時間,能夠經過SystemClock.elapsedRealtime() 獲取(從開機到如今的毫秒數,包括手機的睡眠時間),設備休眠時並不會喚醒設備。
ELAPSED_REALTIME_WAKEUP:與ELAPSED_REALTIME基本功能同樣,只是會在設備休眠時喚醒設備。
RTC:使用絕對時間,能夠經過 System.currentTimeMillis()獲取,設備休眠時並不會喚醒設備。
RTC_WAKEUP: 與RTC基本功能同樣,只是會在設備休眠時喚醒設備。
還有其餘幾個變量是針對API19和特殊方法使用的,這裏就不具體介紹,能夠經過文檔查看。
常用的方法:
對於方法我不會把每個都講一遍,應爲全部的set方法其實都是調用內部的一個private的方法setImpl(),只是不一樣的set方法傳入的值不一樣而已,我就說一下setImpl這個方法。對象
能夠看到setImpl這個方法內部也很簡單,就是判斷一下triggerAtMillis這個參數,而後調用mService.set方法。那這個mService又是什麼呢?blog
在上面找發現了mService是一個叫IAlarmManager的service,這裏變量是紅色的,我用AndroidStudio沒法再跟蹤下去,可是看到這兒咱們應該就想到安卓的AIDL通訊方式,其實AlarmManager就是用的AIDl通訊,從Alarm對象的獲取咱們也能夠看出來排序
AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
是獲取的系統服務,AlarmManager真正的實現都在FrameWork層的AlarmManagerService這個服務中。(從AlarmManager的構造方法中咱們也能夠看到他進行了API19版本的判斷,這裏咱們還要注意mAlwaysExact這個參數,下面會用到)我先將setImpl的幾個參數的含義說一下:
Type:就是咱們上面說的那些變量。
triggerAtMillis:alarm的觸發時間。
windowMillis:這個參數很繁瑣,只有setWindow會主動設置,它的意思是設置一個時間區間,他會在triggerAtMillis的時間開始的這個區間內觸發Alarm通知。普通的set()和setRepeating()方法會經過調用legacyExactLength()這個方法返回這個值:接口
這個方法就用到了咱們上面說的mAlwaysExact這個變量,若是是小於API19的版本會使用
WINDOW_EXACT參數,這個參數是0(意思就是區間設置爲0,那麼就會按照triggerAtMillis這個時間準時觸發,也就是精準觸發)另外一個參數WINDOW_HEURISTIC的值是-1,這個值具體的用法就要看AlarmManagerService具體的實現了,反正只要知道這個值是不精準就能夠。剩下的其餘set方法直接就將這個值設置爲WINDOW_EXACT,那麼那幾個方法就都是精準通知了,除了setInexactRepeating方法將參數設置爲WINDOW_HEURISTIC。開發
intervalMillis:間隔時間,用到Repeating類型的通知時使用,效果就相似咱們設置的鬧鐘,過10分鐘後提醒一次同樣。
operation: 就是PendingIntent,沒什麼說的。
worksource:這個參數我也不知道什麼意思,應爲全部的方法都設置它爲null,有知道的朋友能夠告訴我一下。
alarmClock:設置AlarmClockInfo對象,看代碼裏這個對象實現了Parcelable接口,其實就是對triggerTime和PendingIntent的一個封裝類。文檔
那麼參數就基本說完了,你們只須要不一樣方法傳遞不一樣參數就能夠了。再說一下setAlarmClock這個方法,應爲它用的是WINDOW_EXACT這個參數,因此這個方法也是精準的,不過他有一些侷限性:
1:他只能在API21版本和以後的版本調用。
2:他的Type是固定的RTC_WAKEUP。get
因爲好奇心我又在網上查找了一下AlarmManagerService節能省電的原理是什麼?經過看不少大神的分析,瞭解了原理,這裏我簡單總結一下。若是咱們沒有設置和使用精準通知的話,系統會把觸發時間相近的Alarm放在同一個batch(看名字是一批的意思)中,而後每一個bach根據時間排序放在mAlarmBatchs中,前面的就是先要觸發的alarm。這就是原理, 也就是咱們的Alarm會分爲一批一批的一塊兒觸發,而不是每一個Alarm都要觸發。這就是我上面說的系統中有多個Alarm時會發生時間不許的現象,但若是系統中只有幾個Alarm,而且他們的觸發時間隔得很遠,那麼咱們的Alarm就本身分爲一批,觸發仍是會準的。 好了,對於AlarmManager的分析就到這裏。有不對的地方但願大牛指導。