1.AlarmManager作用
AlarmManager提供了訪問(wèn)系統(tǒng)鬧鐘的服務(wù)莫秆。它允許你安排你的應(yīng)用在未來(lái)某個(gè)時(shí)間點(diǎn)運(yùn)行。這就相當(dāng)于定時(shí)任務(wù)慨蛙。對(duì)于少于60s的定時(shí)任務(wù)梆掸,不推薦使用AlarmManager,可以使用更高效的的Handler來(lái)處理頻發(fā)的任務(wù)虎敦。
2.AlarmManager設(shè)置定時(shí)任務(wù)方法解析
1)set(int type, long triggerAtMillis, PendingIntent operation)
該方法用于設(shè)置一次性鬧鐘游岳。type表示鬧鐘類型。triggerAtMillis表示鬧鐘的觸發(fā)時(shí)間其徙。Operation表示要執(zhí)行的操作胚迫。如果已經(jīng)存在相同的鬧鈴安排要執(zhí)行的Intent(也就是PendingIntent)也相同,那么前一個(gè)被設(shè)置的鬧鈴會(huì)被取消唾那。如果定義鬧鈴的觸發(fā)時(shí)間在過(guò)去访锻,那么鬧鈴會(huì)立即被觸發(fā)。如果已經(jīng)有了這個(gè)意圖的(通過(guò)Intent.filterEquals定義兩個(gè)意圖的相等性)闹获,那么舊的意圖將被刪除并替換為當(dāng)前設(shè)置的意圖期犬。
從API 19開始,鬧鈴的觸發(fā)時(shí)間已經(jīng)不精確了避诽,鬧鈴不會(huì)在設(shè)定的觸發(fā)時(shí)間之前被觸發(fā)龟虎,但是有可能會(huì)出現(xiàn)鬧鈴延遲觸發(fā)的現(xiàn)象。這是為了節(jié)能省電(減少系統(tǒng)喚醒和電池使用)沙庐。一般情況下鲤妥,如果設(shè)置的觸發(fā)時(shí)間是在將來(lái)佳吞,而且時(shí)間不久,鬧鈴一般是不會(huì)被延遲棉安。但只要鬧鈴設(shè)置的觸發(fā)時(shí)間比較長(zhǎng)就很有可能會(huì)被推遲觸發(fā)底扳。
在新的批處理策略下,如果應(yīng)用程序設(shè)置了多個(gè)鬧鈴贡耽,那么這些鬧鈴的實(shí)際觸發(fā)順序可能與它們所請(qǐng)求觸發(fā)時(shí)間的順序不匹配衷模。如果你的應(yīng)用程序有強(qiáng)烈的排序需求,那么你可以使用setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent)來(lái)實(shí)現(xiàn)蒲赂。
2)setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
該方法用于設(shè)置重復(fù)鬧鐘阱冶。type表示鬧鐘類型,triggerAtMillis表示鬧鐘的觸發(fā)時(shí)間凳宙。intervalMillis表示鬧鐘兩次執(zhí)行的間隔時(shí)間熙揍,operation表示鬧鐘執(zhí)行的操作。
當(dāng)調(diào)用這個(gè)方法設(shè)置鬧鈴的時(shí)候氏涩,這個(gè)鬧鈴會(huì)按照設(shè)定的時(shí)間間隔不斷觸發(fā)届囚,直到用cancel顯式地取消這個(gè)鬧鈴。
如果定義的觸發(fā)時(shí)間在過(guò)去是尖,則會(huì)立即觸發(fā)警報(bào)意系,它會(huì)根據(jù)觸發(fā)時(shí)間相對(duì)于重復(fù)間隔的時(shí)間間隔,觸發(fā)鬧鈴計(jì)數(shù)饺汹。
如果一個(gè)鬧鈴被延遲了(例如蛔添,對(duì)于非喚醒類型的鬧鈴遇到系統(tǒng)休眠的時(shí)候),一個(gè)已經(jīng)過(guò)時(shí)的的鬧鈴會(huì)盡快被觸發(fā)兜辞。在此之后迎瞧,未來(lái)的鬧鈴將按原計(jì)劃被觸發(fā);他們不會(huì)隨著時(shí)間的推移而改變。例如逸吵,如果你在每小時(shí)都設(shè)置了一個(gè)重復(fù)的鬧鐘凶硅,但是手機(jī)在7:45到8:45之前已經(jīng)就已經(jīng)休眠了,一旦手機(jī)喚醒扫皱,就會(huì)發(fā)出警報(bào)足绅,然后下一個(gè)鬧鐘將在9點(diǎn)被觸發(fā)。
在API 19中韩脑,所有的重復(fù)鬧鈴都是不準(zhǔn)確的氢妈。如果你的應(yīng)用程序需要精確的觸發(fā)時(shí)間,那么它必須使用一次性的警報(bào)段多,當(dāng)鬧鈴被觸發(fā)后重新再重新安排一個(gè)相同時(shí)間間隔的鬧鈴首量。
3) setInexactRepeating(int type, long triggerAtMillis,long intervalMillis, PendingIntent operation)
該方法也用于設(shè)置重復(fù)鬧鐘。type表示鬧鐘類型,triggerAtMillis表示鬧鐘的觸發(fā)時(shí)間加缘。intervalMillis表示鬧鐘兩次執(zhí)行的間隔時(shí)間粥航,operation表示鬧鐘執(zhí)行的操作。這個(gè)方法和setRepeating差不多生百,但是它是不準(zhǔn)確的,它主要用于對(duì)觸發(fā)時(shí)間要求不嚴(yán)格的重復(fù)任務(wù)柄延。相對(duì)于setRepeating方法蚀浆,也更加節(jié)能。因?yàn)橄到y(tǒng)會(huì)將差不多的鬧鐘進(jìn)行合并搜吧,以避免在不必要地喚醒設(shè)備市俊。
在API 19中,所有的重復(fù)警報(bào)都是不準(zhǔn)確的滤奈。
你的鬧鈴的第一個(gè)觸發(fā)器將不會(huì)在請(qǐng)求的時(shí)間之前被觸發(fā)摆昧,但是在此之后可能不會(huì)出現(xiàn)一個(gè)完整的時(shí)間間隔。此外蜒程,盡管重復(fù)報(bào)警的總周期是按要求進(jìn)行的绅你,但兩次連續(xù)兩次的警報(bào)之間的間隔時(shí)間可能會(huì)有所不同。如果你的應(yīng)用程序要求時(shí)間間隔比較嚴(yán)格昭躺,那么可以使用一個(gè)適當(dāng)?shù)拇翱趤?lái)調(diào)用一個(gè)鬧鈴; setWindow(int, long, long, PendingIntent) 和 setExact(int, long, PendingIntent).
4) setWindow(int type, long windowStartMillis, long windowLengthMillis,
PendingIntent operation)
設(shè)置一個(gè)鬧鐘在給定的時(shí)間窗觸發(fā)忌锯。類似于set,該方法允許應(yīng)用程序精確地控制操作系統(tǒng)調(diào)整鬧鐘觸發(fā)時(shí)間的程度领炫。
這里的時(shí)間窗是什么意思偶垮?帶著疑問(wèn)到網(wǎng)絡(luò)上找了一下,貌似時(shí)間窗這個(gè)概念是股票領(lǐng)域一個(gè)叫江恩的人提出的帝洪,網(wǎng)上解釋說(shuō)時(shí)間窗口有兩個(gè)含義:
一是指時(shí)間周期運(yùn)行到一定的階段時(shí)似舵,便會(huì)發(fā)生轉(zhuǎn)折;
二是指在時(shí)間周期中的某一關(guān)鍵時(shí)間段,如果對(duì)特定事物施加影響或采取某種行動(dòng)葱峡,成功幾率將增加砚哗。
一開始我認(rèn)為這是一個(gè)時(shí)間區(qū)間的概念,也就是說(shuō)鬧鈴會(huì)在[windowStartMillis, windowLengthMillis]區(qū)間內(nèi)的某個(gè)時(shí)間觸發(fā)族沃。但是多次測(cè)試發(fā)現(xiàn)觸發(fā)時(shí)間會(huì)出現(xiàn)在區(qū)間之外频祝。所以我也弄不清楚windowStartMillis和windowLengthMillis的作用了,可以確定的是鬧鈴觸發(fā)時(shí)間肯定是在windowStartMillis之后的脆淹。有清楚的小伙伴麻煩說(shuō)一下常空。
type表示鬧鐘類型,windowStartMillis表示時(shí)間窗的開始點(diǎn)盖溺。windowLengthMillis表示時(shí)間窗的長(zhǎng)度漓糙,operation表示鬧鐘執(zhí)行的操作。
該方法還可用于在多個(gè)鬧鈴中實(shí)現(xiàn)嚴(yán)格的排序保證烘嘱,確保每個(gè)鬧鈴的請(qǐng)求不交叉昆禽。
當(dāng)不需要精確的觸發(fā)時(shí)蝗蛙,應(yīng)用程序應(yīng)該使用set(int,long,PendingIntent)方法。這將給操作系統(tǒng)最大的靈活性醉鳖,以最小化喚醒系統(tǒng)和電池使用捡硅。對(duì)于必須在精確指定的時(shí)間觸發(fā)鬧鈴,應(yīng)用程序可以使用setExact(int,long,PendingIntent)盗棵。
我們需要注意的是setWindow方法是在API>19后才能調(diào)用的壮韭。
5) setExact(int type, long triggerAtMillis, PendingIntent operation)
設(shè)置一個(gè)鬧鐘在給定的時(shí)間內(nèi)準(zhǔn)確的觸發(fā)。這種方法就像set(int, long, PendingIntent),但不允許操作系統(tǒng)調(diào)整觸發(fā)時(shí)間纹因。鬧鈴將盡可能接近給定的觸發(fā)時(shí)間點(diǎn)觸發(fā)喷屋。type表示鬧鐘類型,triggerAtMillis表示鬧鐘的觸發(fā)時(shí)間瞭恰。operation表示鬧鐘執(zhí)行的操作屯曹。
只有當(dāng)迫切需要在鬧鈴在準(zhǔn)確的時(shí)間內(nèi)觸發(fā)才調(diào)用這個(gè)方法。但是不鼓勵(lì)應(yīng)用程序調(diào)用準(zhǔn)確的鬧鈴,因?yàn)樗麄儨p少了操作系統(tǒng)的運(yùn)算能力和耗電惊畏。
這個(gè)方法是在API>19后才能調(diào)用的恶耽。
6)設(shè)置鬧鈴的方法中的type
AlarmManager.RTC,該狀態(tài)下鬧鐘使用絕對(duì)時(shí)間颜启,即當(dāng)前系統(tǒng)時(shí)間驳棱,不喚醒手機(jī)(也可能是其它設(shè)備)休眠;當(dāng)手機(jī)休眠時(shí)不觸發(fā)鬧鐘农曲。
AlarmManager.RTC_WAKEUP社搅,該狀態(tài)下鬧鐘使用絕對(duì)時(shí)間,即當(dāng)前系統(tǒng)時(shí)間乳规,當(dāng)鬧鐘發(fā)躰時(shí)喚醒休眠狀態(tài)的手機(jī)形葬;
AlarmManager.ELAPSED_REALTIME,該狀態(tài)下鬧鐘使用相對(duì)時(shí)間(相對(duì)于系統(tǒng)啟動(dòng)開始)暮的,不喚醒手機(jī)休眠笙以;當(dāng)手機(jī)休眠時(shí)不觸發(fā)鬧鐘。
AlarmManager.ELAPSED_REALTIME_WAKEUP冻辩,該狀態(tài)下鬧鐘使用相對(duì)時(shí)間(相對(duì)于系統(tǒng)啟動(dòng)開始)猖腕,當(dāng)鬧鐘觸發(fā)時(shí)喚醒休眠狀態(tài)的手機(jī);
RTC鬧鐘和ELAPSED_REALTIME最大的差別就是前者可以通過(guò)修改手機(jī)時(shí)間觸發(fā)鬧鐘事件恨闪,后者要通過(guò)真實(shí)時(shí)間的流逝倘感,即使在休眠狀態(tài),時(shí)間也會(huì)被計(jì)算咙咽。
7)其他
Android 6.0最大變化之一就是加入了新的電量管理模式:休眠模式老玛,當(dāng)設(shè)備一段時(shí)間不用的時(shí)候,當(dāng)屏幕關(guān)閉的時(shí)候,系統(tǒng)會(huì)自動(dòng)進(jìn)入休眠模式蜡豹。這樣所有的App都將進(jìn)入掛起模式麸粮,不能進(jìn)行接入網(wǎng)絡(luò)等一些操作。
當(dāng)然系統(tǒng)也會(huì)定期的退出休眠模式镜廉,來(lái)完成App延遲的工作弄诲,在這個(gè)空窗期(我暫且就這么叫),系統(tǒng)會(huì)運(yùn)行所有同步娇唯,工作威根,提醒等,并允許app接入網(wǎng)絡(luò)视乐。
當(dāng)過(guò)了空窗期后,系統(tǒng)會(huì)重新進(jìn)入休眠期敢茁,App也會(huì)隨著掛起狀態(tài)佑淀,隨著時(shí)間的推移,空窗期越來(lái)越少彰檬,是為了幫助沒有接入充電器的長(zhǎng)期閑置的設(shè)備減少電池消耗伸刃。
只要用戶喚醒設(shè)備,打開屏幕或者接入電源逢倍,系統(tǒng)會(huì)自動(dòng)退出休眠模式捧颅,所有的活動(dòng)都會(huì)恢復(fù)正常狀態(tài)。
下面是當(dāng)進(jìn)入休眠期時(shí)的約束:
- 延遲網(wǎng)絡(luò)請(qǐng)求较雕;
- 系統(tǒng)忽略喚醒鎖碉哑;
- 標(biāo)準(zhǔn)的鬧鐘提醒(包括setExat()和setWindow())會(huì)被延時(shí)到下一個(gè)空窗期;
如果一定要在休眠期喚醒鬧鐘亮蒋,可以用setAndAllowWhileIdle()喚醒不精確鬧鐘或者setExactAndAllowWhileIdle()喚醒精確鬧鐘
鬧鐘設(shè)置setAlarmClock()則繼續(xù)保持常態(tài)扣典,在喚醒這個(gè)鬧鐘前系統(tǒng)會(huì)退出休眠期一段時(shí)間; - 禁用wifi scan慎玖;
- 不允許同步贮尖;
- 禁用JobScheduler ;
休眠容易影響 AlarmManager alarms and timers manage趁怔,因?yàn)楫?dāng)系統(tǒng)進(jìn)入休眠狀態(tài)湿硝,鬧鐘在android5.1(API level 22)或者更低不會(huì)喚醒 ;
為了幫助管理alarms,android 6.0(API level 23) 介紹了兩個(gè)方法: setAndAllowWhileIdle()和setExactAndAllowWhileIdle(). 這樣即使你再休眠期的時(shí)候 鬧鐘也會(huì)被喚醒润努;
PS: 即使這兩種方法关斜,每個(gè)App每15分鐘喚醒次數(shù)也不能超過(guò)一次;
有了休眠铺浇,自然會(huì)影響我們的后臺(tái)服務(wù)蚤吹,比如 推送,google 建議是用GMC( Google Cloud Messaging)。
3.代碼解析(關(guān)于使用AlarmManager實(shí)現(xiàn)定時(shí)任務(wù)的例子)
首先裁着,初始化一個(gè)鬧鈴
//這個(gè)方法我是在Acitivity中定義的繁涂,這里舉例設(shè)置鬧鈴用的是set方法
private void initAlarmManager()
{
//創(chuàng)建Intent對(duì)象,action為ELITOR_CLOCK二驰,附加信息為字符串“點(diǎn)擊我了”
Intent intent = new Intent("ELITOR_CLOCK");
intent.putExtra("msg","點(diǎn)擊我了");
//定義一個(gè)PendingIntent對(duì)象扔罪,PendingIntent.getBroadcast包含了sendBroadcast的動(dòng)作。
//PendingIntent不僅可以發(fā)給廣播桶雀,還可以是
//getActivity(context, requestCode, intent, flags)
//getActivities(context, requestCode, intents, flags)
//getService(context, requestCode, intent, flags)
//只需要AndroidMenifest.xml中注冊(cè)的組件里的意圖過(guò)濾器中添加對(duì)應(yīng)的action標(biāo)簽即可矿酵。
//也就是發(fā)送了action 為"ELITOR_CLOCK"的intent
PendingIntent pi = PendingIntent.getBroadcast(this,0,intent,0);
//AlarmManager對(duì)象,注意這里并不是new一個(gè)對(duì)象,Alarmmanager為系統(tǒng)級(jí)服務(wù)
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
//設(shè)置鬧鐘從當(dāng)前時(shí)間開始矗积,每隔5s執(zhí)行一次PendingIntent對(duì)象pi全肮,注意第一個(gè)參數(shù)與第二個(gè)參數(shù)的關(guān)系
// 5秒后通過(guò)PendingIntent pi對(duì)象發(fā)送廣播
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,5*1000,pi);
}
然后,我們需要定義一個(gè)廣播用來(lái)處理鬧鈴被觸發(fā)后要處理的事務(wù)
public class MyReceiver extends BroadcastReceiver {
private static final int NOTIFICATION_FLAG = 1;
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
LogUtil.init("TestAlarmManager", "alarmLog.txt");
//這里獲取的是上面鬧鈴傳遞的值“點(diǎn)擊我了”
String msg = intent.getStringExtra("msg");
Log.i("lgy", "onclock......................" + msg);
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
定義了廣播之后別忘了注冊(cè)廣播
4.源碼地址
http://download.csdn.net/download/lgywsdy/9995903
5.參考文章
https://plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqo