天天看點

鬧鐘管理器AlarmManager(整理)

       在Android中,可以使用鬧鐘管理器來觸發事件,包括廣播BroadcastReceiver,服務Service和活動Activity。這些事件可以在特定的時刻或者以固定的時間間隔來觸發。

       使用鬧鐘管理器一般有以下幾個步驟:

        1、擷取到鬧鐘管理器的服務,即AlarmManager;

        2、确定設定鬧鐘的時刻;

        3、建立要調用的接收程式,可以是廣播BroadcastReceiver,服務Service和活動Activity;

        4、建立一個挂起的Intent(即PendingIntent),它可傳遞給鬧鐘管理器來調用設定的該接收程式;

        5、使用第2步中的時間和第4步中的Intent來設定鬧鐘;

        6、在第3步中的接收鬧鐘管理器的調用。

        接下來是對每個步驟進行說明:

1、擷取鬧鐘管理器,AlarmManager

      這是比較簡單的,調用系統服務就可以得到AlarmManager。

AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
           

2、建立鬧鐘的時刻

      為了快捷友善,特别建立了一個Utils類,在裡面提供建立各種類型的時刻。

public class Utils 
{		
	/**
	 * 建立secs秒後鬧鐘時間
	 * @param secs
	 * @return
	 */
	public static Calendar getTimeAfterInSecs(int secs)
	{
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.SECOND,secs);
		return cal;
	}
	public static Calendar getCurrentTime()
	{
		Calendar cal = Calendar.getInstance();
		return cal;
	}
	/**
	 * 建立在某個固定小時時刻的鬧鐘
	 * @param hours
	 * @return
	 */
	public static Calendar getTodayAt(int hours)
	{
		Calendar today = Calendar.getInstance();
		Calendar cal = Calendar.getInstance();
		cal.clear();
		
		int year = today.get(Calendar.YEAR);
		int month = today.get(Calendar.MONTH);
		//represents the day of the month
		int day = today.get(Calendar.DATE);
		cal.set(year,month,day,hours,0,0);
		return cal;
	}
	public static String getDateTimeString(Calendar cal)
	{
		SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss");
		df.setLenient(false);
		String s = df.format(cal.getTime());
		return s;
	}
}
           

3、建立接受鬧鐘的接收程式,這裡暫時使用一種類型BroadCastReceiver

public class TestReceiver extends BroadcastReceiver 
{
    private static final String tag = "TestReceiver"; 
    @Override
    public void onReceive(Context context, Intent intent) 
    {
        Log.d("TestReceiver", "intent=" + intent);
        String message = intent.getStringExtra("message");
        Log.d(tag, message);
    }
}
           

4、建立鬧鐘的PendingIntent

      首先需要建立隻想TestReceiver的Intent

Intent intent = new Intent(mContext, TestReceiver.class);
    	intent.putExtra("message", "Single Shot Alarm");
           

    接下來建立挂起的PendingIntent:

PendingIntent pi = 
    		PendingIntent.getBroadcast(
    		  mContext, 	//context
              1, 		    //request id, used for disambiguating this intent
              intent, 	    //intent to be delivered 
              PendingIntent.FLAG_ONE_SHOT);  //pending intent flags
           

5、設定鬧鐘

Calendar cal = Utils.getTimeAfterInSecs(30);
AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pi);
           

這樣就可以在TestReiver中接收到鬧鐘消息了。

以上是關于鬧鐘管理器的簡單使用,接下來将增加關于設定重複鬧鐘和取消鬧鐘的使用。

一、設定重複鬧鐘

     設定重複鬧鐘的前面幾個步驟都是一樣的,隻是在最後設定鬧鐘的時候有所變化

am.setRepeating(AlarmManager.RTC_WAKEUP, 
    			cal.getTimeInMillis(), 
    			5*1000, //5 secs 
    			pi);
           

     關于這個方法的參數說明:第一個:警報的類型,這裡采用鬧鐘來喚醒裝置;第二個:第一次鬧鐘執行的時刻;第三個:在鬧鐘第一次執行後,每隔多久開始重複執行;第四個:挂起的PendingIntent。

二、取消鬧鐘

     要取消鬧鐘,必須首先挂起一個Intent,然後調用cancel()方法,将參數傳遞給鬧鐘管理器。

Intent intent = 
    		new Intent(this.mContext, TestReceiver.class);
    	
    	//To cancel, extra is not necessary to be filled in
    	//intent.putExtra("message", "Repeating Alarm");

    	PendingIntent pi = this.getDistinctPendingIntent(intent, 2);
    	
    	// Schedule the alarm!
    	AlarmManager am = 
    		(AlarmManager)
    		   this.mContext.getSystemService(Context.ALARM_SERVICE);
    	am.cancel(pi);
           
protected PendingIntent getDistinctPendingIntent(Intent intent, int requestId)
    {
    	PendingIntent pi = 
    		PendingIntent.getBroadcast(
    		  mContext, 	//context
              requestId, 	//request id
              intent, 		//intent to be delivered
              0);

        //pending intent flags
        //PendingIntent.FLAG_ONE_SHOT);    	
    	return pi;
    }
           

      在建立Intent的時候,如果是為了取消鬧鐘,可以不用再Intent中設定任何消息參數和資料,隻需要保證最後的指向接收程式一緻。然後建立的PendingIntent也必須和原來一緻,必須確定采用設定鬧鐘時相同的方式來構造它,包括請求代碼,即上面的requestId和接收程式。

三、設定多個鬧鐘

      如果了解了上面關于設定單個鬧鐘和重複鬧鐘的設定過程,可能會認為隻需要建立多個不同的cal時刻,然後分别設定到鬧鐘裡面,就能實作了。其實不然,裡面涉及到一些陷進。

先看下面的代碼:

/*
	 * Same intent cannot be scheduled multiple times.
	 * If you do, only the last one will take affect.
	 * 
	 * Notice you are using the same request id.
	 */
    public void scheduleSameIntentMultipleTimes()
    {
    	//Get the instance in time that is 
    	//30 secs from now.
    	Calendar cal = Utils.getTimeAfterInSecs(30);
    	Calendar cal2 = Utils.getTimeAfterInSecs(35);
    	Calendar cal3 = Utils.getTimeAfterInSecs(40);
    	Calendar cal4 = Utils.getTimeAfterInSecs(45);
    	
    	//If you want to point to 11:00 hours today.
    	//Calendar cal = Utils.getTodayAt(11);
    	
    	//Get an intent to invoke
    	//TestReceiver class
    	Intent intent = new Intent(mContext, TestReceiver.class);
    	intent.putExtra("message", "Single Shot Alarm");
    	PendingIntent pi = this.getDistinctPendingIntent(intent, 1);
    	
    	// Schedule the alarm!
    	AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
    	
    	am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pi);
    	am.set(AlarmManager.RTC_WAKEUP, cal2.getTimeInMillis(), pi);
    	am.set(AlarmManager.RTC_WAKEUP, cal3.getTimeInMillis(), pi);
    	am.set(AlarmManager.RTC_WAKEUP, cal4.getTimeInMillis(), pi);
    }
           

      上面這部分代碼,使用了同一個PendingIntent,設定了4個不同的鬧鐘時間,這樣的效果就是: 隻有最後一個鬧鐘會被觸發,前面的所有鬧鐘都會被忽略掉。

而要實作多個鬧鐘的實作是如下:

/*
	 * Same intent can be scheduled multiple times
	 * if you change the request id on the pending intent.
	 * Request id identifies an intent as a unique intent.
	 */
    public void scheduleDistinctIntents()
    {
    	//Get the instance in time that is 
    	//30 secs from now.
    	Calendar cal = Utils.getTimeAfterInSecs(30);
    	Calendar cal2 = Utils.getTimeAfterInSecs(35);
    	Calendar cal3 = Utils.getTimeAfterInSecs(40);
    	Calendar cal4 = Utils.getTimeAfterInSecs(45);
    	
    	//If you want to point to 11:00 hours today.
    	//Calendar cal = Utils.getTodayAt(11);

    	//Get an intent to invoke
    	//TestReceiver class
    	Intent intent = new Intent(mContext, TestReceiver.class);
    	intent.putExtra("message", "Single Shot Alarm");

    	// Schedule the alarms!
    	AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
    	
    	am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), getDistinctPendingIntent(intent,1));
    	am.set(AlarmManager.RTC_WAKEUP, cal2.getTimeInMillis(), getDistinctPendingIntent(intent,2));
    	am.set(AlarmManager.RTC_WAKEUP,	cal3.getTimeInMillis(), getDistinctPendingIntent(intent,3));
    	am.set(AlarmManager.RTC_WAKEUP,	cal4.getTimeInMillis(), getDistinctPendingIntent(intent,4));
    }
           

      每個挂起的Intent,也就是PendingIntent不同,也就是讓每個PendingIntent的RequestId不同。産生這種原因是因為在AlarmManager中設定鬧鐘時,對于相同的Intent,是通過不同的請求Id來建立的。

    在相同類型的Intent上設定鬧鐘時,隻有最後一個鬧鐘時生效的。

     以下是AlarmMangerService的部分源碼,可以看到為什麼不能使用相同的PendingIntent了。

public void setRepeating(int type, long triggerAtTime, long interval, 
            PendingIntent operation) {
.....
synchronized (mLock) {
            Alarm alarm = new Alarm();
            alarm.type = type;
            alarm.when = triggerAtTime;
            alarm.repeatInterval = interval;
            alarm.operation = operation;

            // Remove this alarm if already scheduled.
            removeLocked(operation);  //當使用相同的operation的時候u,就會先把已有的删除,這樣的話,直到最後一個才會生效。

            if (localLOGV) Slog.v(TAG, "set: " + alarm);

            int index = addAlarmLocked(alarm);
            if (index == 0) {
                setLocked(alarm);
            }
        }
    }

           

最後一點是關于鬧鐘的持久化問題,它們是不能儲存到裝置重新啟動之後,也就是說當裝置重新啟動後,之前設定的鬧鐘将全部失效。

下面是一個詳細的鬧鐘例子,可以作為參考

http://pan.baidu.com/s/1kTHlOG3

下面是關于android中鬧鐘的調用流程,知道進入到Android的底層,可以看看:

http://blog.csdn.net/crycheng/article/details/7804813