AlarmManager定時鬧鐘
一、AlarmManager介紹:
AlarmManager是Android中常用的一種系統級別的提示服務,在特定的時刻為我們廣播一個指定的Intent。簡單的說就是我們設定一個時間,然後在該時間到來時,AlarmManager為我們廣播一個我們設定的Intent,通常我們使用 PendingIntent,PendingIntent可以理解為Intent的封裝包,簡單的說就是在Intent上在加個指定的動作。在使用Intent的時候,我們還需要在執行startActivity、startService或sendBroadcast才能使Intent有用。而PendingIntent的話就是將這個動作包含在內了。
二、AlarmManager的常用方法有三個:
(1)set(int type,long startTime,PendingIntent pi);
該方法用於設置一次性鬧鐘,第一個參數表示鬧鐘類型,第二個參數表示鬧鐘執行時間,第三個參數表示鬧鐘響應動作。
(2)setRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
該方法用於設置重復鬧鐘,第一個參數表示鬧鐘類型,第二個參數表示鬧鐘首次執行時間,第三個參數表示鬧鐘兩次執行的間隔時間,第三個參數表示鬧鐘響應動作。
(3)setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
該方法也用於設置重復鬧鐘,與第二個方法相似,不過其兩個鬧鐘執行的間隔時間不是固定的而已。
三個方法各個參數詳悉:
(1)int type: 鬧鐘的類型,常用的有5個值:AlarmManager.ELAPSED_REALTIME、 AlarmManager.ELAPSED_REALTIME_WAKEUP、AlarmManager.RTC、 AlarmManager.RTC_WAKEUP、AlarmManager.POWER_OFF_WAKEUP。
AlarmManager.ELAPSED_REALTIME表示鬧鐘在手機睡眠狀態下不可用,該狀態下鬧鐘使用相對時間(相對於系統啟動開始),狀態值為3;
AlarmManager.ELAPSED_REALTIME_WAKEUP表示鬧鐘在睡眠狀態下會喚醒系統並執行提示功能,該狀態下鬧鐘也使用相對時間,狀態值為2;
AlarmManager.RTC表示鬧鐘在睡眠狀態下不可用,該狀態下鬧鐘使用絕對時間,即當前系統時間,狀態值為1;
AlarmManager.RTC_WAKEUP表示鬧鐘在睡眠狀態下會喚醒系統並執行提示功能,該狀態下鬧鐘使用絕對時間,狀態值為0;
AlarmManager.POWER_OFF_WAKEUP表示鬧鐘在手機關機狀態下也能正常進行提示功能,所以是5個狀態中用的最多的狀態之一,該狀態下鬧鐘也是用絕對時間,狀態值為4;不過本狀態好像受SDK版本影響,某些版本並不支持;
(2)long startTime: 鬧鐘的第一次執行時間,以毫秒為單位,可以自定義時間,不過一般使用當前時間。需要註意的是,本屬性與第一個屬性(type)密切相關,(3)long intervalTime:對於後兩個方法來說,存在本屬性,表示兩次鬧鐘執行的間隔時間,也是以毫秒為單位。
(4)PendingIntent pi: 綁定了鬧鐘的執行動作,比如發送一個廣播、給出提示等等。PendingIntent是Intent的封裝類。需要註意的是,如果是通過啟動服務來實現鬧鐘提 示的話,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)方法。如果這三種方法錯用了的話,雖然不會報錯,但是看不到鬧鐘提示效果。
屬性或方法名稱 | 說明 |
ELAPSED_REALTIME | 設置鬧鐘時間,從系統啟動開 始 |
ELAPSED_REALTIME_WAKEUP | 同上,如果設備休眠則喚醒 |
INTERVAL_DAY | 設置鬧鐘,間隔一天 |
INTERVAL_HALF_DAY | 設置鬧鐘,間隔半天 |
INTERVAL_FIFTEEN_MINUTES | 設置鬧鐘,間隔15分鐘 |
INTERVAL_HALF_HOUR | 設置鬧鐘,間隔半個小時 |
INTERVAL_HOUR | 設置鬧鐘,間隔一個小時 |
RTC | 設置鬧鐘時間從系統當前時間開(System.currentTimeMillis()) |
RTC_WAKEUP | 同上,設備休眠則喚醒 |
set(int type,long triggerAtTime,PendingIntent operation) | 設置在某個時間執行鬧鐘 |
setRepeating(int type,long triggerAtTime,long interval PendingIntent operation) | 設置在某個時間重復執行鬧鐘 |
setInexactRepeating(int type,long triggerAtTime,long interval PendingIntent operation) | 設置在某個時間重復執行鬧鐘,但不是間隔固定時間 |
註意:
- 設置鬧鐘使用AlarmManager.set()函數,它的triggerAtTime參數,如果要用Calendar.getTimesInMillis()獲得,就必須先設置Calendar對象,例如要讓鬧鐘在當天的16:30分啟動,就要設置HOUR_OF_DAY(16)、MINUTE(30)、MILLISECOND(0),特別是HOUR_OF_DAY,我一開始誤用了HOUR,這是12進制計時方法,HOUR_OF_DAY是24進制計時方法。
- 針對同一個PendingIntent,AlarmManager.set()函數不能設置多個alarm。調用該函數時,假如已經有old alarm使用相同的PendingIntent,會先取消(cancel)old alarm,然後再設置新的alarm。如何判斷是否已經有相同的PendingIntent,請看下條。
- 取消alarm使用AlarmManager.cancel()函數,傳入參數是個PendingIntent實例。該函數會將所有跟這個PendingIntent相同的Alarm全部取消,怎麽判斷兩者是否相同,android使用的是intent.filterEquals(),具體就是判斷兩個PendingIntent的action、data、type、class和category是否完全相同。
- 在AndroidManifest.xml中靜態註冊BroadcastReceiver時,一定使用android:process=":xxx"屬性,因為SDK已註明:If the name assigned to this attribute begins with a colon (‘:‘), a new process, private to the application, is created when it‘s needed and the broadcast receiver runs in that process.
-
在此討論一下process屬性,它規定了組件(activity, service, receiver等)所在的進程。
通常情況下,沒有指定這個屬性,一個應用所有的組件都運行在應用的默認進程中,進程的名字和應用的包名一致。
比如manifest的package="com.example.helloalarm",則默認進程名就是com.example.helloalarm。
<application>元素的process屬性可以為全部的組件設置一個不同的默認進程。
組件可以override這個默認的進程設置,這樣你的應用就可以是多進程的。 如果你的process屬性以一個冒號開頭,進程名會在原來的進程名之後附加冒號之後的字符串作為新的進程名。當組件需要時,會自動創建這個進程。這個進程是應用私有的進程。如果process屬性以小寫字母開頭,將會直接以屬性中的這個名字作為進程名,這是一個全局進程,這樣的進程可以被多個不同應用中的組件共享。
使用:
三、AlarmManager的使用步驟
1)獲得ALarmManager實例 ALarmManager am=(ALarmManager)getSystemService(ALARM_SERVICE);
2)定義一個PendingIntent發出廣播
3)調用ALarmManager方法,設置定時或重復提醒
4)取消提醒:
該函數會將所有跟這個PendingIntent相同的Alarm全部取消,怎麽判斷兩者是否相同,android使用的是intent.filterEquals(),具體就是判斷兩個PendingIntent的action、data、type、class和category是否完全相同。
四、實例
通過Service和BroadcastReceiver實現定時任務
(1)寫一個Service執行定時發廣播操作
public class AlarmService extends Service { @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO: 16/3/28 如果執行費時操作,可以新起線程,但是覺得線程太多不好 // new Thread(new Runnable() { // @Override // public void run() { // } // }).start(); Log.e("test", "這是一條測試信息"); AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE); int minute = 1000; long triggerAtTime = SystemClock.elapsedRealtime() + minute; 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); } }
(2)寫一個BroadcastReceiver接收廣播啟動Service
public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent i = new Intent(context, AlarmService.class); context.startService(i); } }
(3)最後在Activity中實現循環
/** * 這個Alarm的循環只能給Service用 * Activity啟動Service,Service發送廣播,廣播接收器接收廣播並啟動Service,Service再次發送廣播,如此重復 */ public class AlarmActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_alarm); Intent i = new Intent(this, AlarmService.class); startService(i); } @Override protected void onDestroy() { super.onDestroy(); Intent i = new Intent(this, AlarmService.class); stopService(i); } }
最後,假如需要對UI進行操作,完全可以將BroadcastReceiver寫在Activity中,來實現定時的UI操作。一直啟動Service我不覺得是一個好的解決方案,可以定制重復鬧鐘
參考:https://blog.csdn.net/Jack_EUSong/article/details/51027215
https://www.cnblogs.com/ProtectedDream/p/6351447.html
https://blog.csdn.net/violetjack0808/article/details/50982604
https://blog.csdn.net/wds1181977/article/details/51154026
AlarmManager定時鬧鐘