在Android中,可以使用闹钟管理器来触发事件,包括广播BroadcastReceiver,服务Service和活动Activity。这些事件可以在特定的时刻或者以固定的时间间隔来触发。

       使用闹钟管理器一般有以下几个步骤:

        1、获取到闹钟管理器的服务,即AlarmManager;

        2、确定设置闹钟的时刻;

        3、创建要调用的接收程序,可以是广播BroadcastReceiver,服务Service和活动Activity;

        4、创建一个挂起的Intent(即PendingIntent),它可传递给闹钟管理器来调用设置的该接收程序;

        5、使用第2步中的时间和第4步中的Intent来设置闹钟;

        6、在第3步中的接收闹钟管理器的调用。

        接下来是对每个步骤进行说明:

1、获取闹钟管理器,AlarmManager

      这是比较简单的,调用系统服务就可以得到AlarmManager。



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



    2、创建闹钟的时刻

          为了快捷方便,特别创建了一个Utils类,在里面提供创建各种类型的时刻。


    1. public class Utils   
    2. {         
    3. /**
    4.      * 创建secs秒后闹钟时间
    5.      * @param secs
    6.      * @return
    7.      */  
    8. public static Calendar getTimeAfterInSecs(int secs)  
    9.     {  
    10.         Calendar cal = Calendar.getInstance();  
    11.         cal.add(Calendar.SECOND,secs);  
    12. return cal;  
    13.     }  
    14. public static Calendar getCurrentTime()  
    15.     {  
    16.         Calendar cal = Calendar.getInstance();  
    17. return cal;  
    18.     }  
    19. /**
    20.      * 创建在某个固定小时时刻的闹钟
    21.      * @param hours
    22.      * @return
    23.      */  
    24. public static Calendar getTodayAt(int hours)  
    25.     {  
    26.         Calendar today = Calendar.getInstance();  
    27.         Calendar cal = Calendar.getInstance();  
    28.         cal.clear();  
    29.           
    30. int year = today.get(Calendar.YEAR);  
    31. int month = today.get(Calendar.MONTH);  
    32. //represents the day of the month  
    33. int day = today.get(Calendar.DATE);  
    34. 0,0);  
    35. return cal;  
    36.     }  
    37. public static String getDateTimeString(Calendar cal)  
    38.     {  
    39. new SimpleDateFormat("MM/dd/yyyy hh:mm:ss");  
    40. false);  
    41.         String s = df.format(cal.getTime());  
    42. return s;  
    43.     }  
    44. }


    3、创建接受闹钟的接收程序,这里暂时使用一种类型BroadCastReceiver


     

    1. public class TestReceiver extends BroadcastReceiver   
    2. {  
    3. private static final String tag = "TestReceiver";   
    4. @Override  
    5. public void onReceive(Context context, Intent intent)   
    6.     {  
    7. "TestReceiver", "intent=" + intent);  
    8. "message");  
    9.         Log.d(tag, message);  
    10.     }  
    11. }


    4、创建闹钟的PendingIntent

          首先需要创建只想TestReceiver的Intent


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

        接下来创建挂起的PendingIntent:



    1. PendingIntent pi =   
    2. ndingIntent.getBroadcast(  
    3. mContext,   //context  
    4. 1,            //request id, used for disambiguating this intent  
    5. //intent to be delivered   
    6. //pending intent flags


    5、设置闹钟


     

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


    这样就可以在TestReiver中接收到闹钟消息了。

    以上是关于闹钟管理器的简单使用,接下来将增加关于设置重复闹钟和取消闹钟的使用。

    一、设置重复闹钟

         设置重复闹钟的前面几个步骤都是一样的,只是在最后设置闹钟的时候有所变化


     

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


         关于这个方法的参数说明:第一个:警报的类型,这里采用闹钟来唤醒设备;第二个:第一次闹钟执行的时刻;第三个:在闹钟第一次执行后,每隔多久开始重复执行;第四个:挂起的PendingIntent。

    二、取消闹钟

         要取消闹钟,必须首先挂起一个Intent,然后调用cancel()方法,将参数传递给闹钟管理器。


     

    1. Intent intent =   
    2. new Intent(this.mContext, TestReceiver.class);  
    3.       
    4. //To cancel, extra is not necessary to be filled in  
    5. //intent.putExtra("message", "Repeating Alarm");  
    6.   
    7. this.getDistinctPendingIntent(intent, 2);  
    8.       
    9. // Schedule the alarm!  
    10.     AlarmManager am =   
    11.         (AlarmManager)  
    12. this.mContext.getSystemService(Context.ALARM_SERVICE);  
    13.     am.cancel(pi);  


     

     
     
    1. protected PendingIntent getDistinctPendingIntent(Intent intent, int requestId)  
    2.     {  
    3.         PendingIntent pi =   
    4.             PendingIntent.getBroadcast(  
    5. //context  
    6. //request id  
    7. //intent to be delivered  
    8. 0);  
    9.   
    10. //pending intent flags  
    11. //PendingIntent.FLAG_ONE_SHOT);       
    12. return pi;  
    13.     }


          在创建Intent的时候,如果是为了取消闹钟,可以不用再Intent中设置任何消息参数和数据,只需要保证最后的指向接收程序一致。然后创建的PendingIntent也必须和原来一致,必须确保采用设置闹钟时相同的方式来构造它,包括请求代码,即上面的requestId和接收程序。

    三、设置多个闹钟

          如果了解了上面关于设置单个闹钟和重复闹钟的设置过程,可能会认为只需要创建多个不同的cal时刻,然后分别设置到闹钟里面,就能实现了。其实不然,里面涉及到一些陷进。

    先看下面的代码:



     

     
     
    1. /*
    2.      * Same intent cannot be scheduled multiple times.
    3.      * If you do, only the last one will take affect.
    4.      * 
    5.      * Notice you are using the same request id.
    6.      */  
    7. public void scheduleSameIntentMultipleTimes()  
    8.     {  
    9. //Get the instance in time that is   
    10. //30 secs from now.  
    11. 30);  
    12. 35);  
    13. 40);  
    14. 45);  
    15.           
    16. //If you want to point to 11:00 hours today.  
    17. //Calendar cal = Utils.getTodayAt(11);  
    18.           
    19. //Get an intent to invoke  
    20. //TestReceiver class  
    21. "color:#FF0000;">Intent intent = new Intent(mContext, TestReceiver.class);  
    22. "message", "Single Shot Alarm");</span>  
    23. "color:#FF0000;">PendingIntent pi = this.getDistinctPendingIntent(intent, 1);</span>  
    24.           
    25. // Schedule the alarm!  
    26.         AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);  
    27.           
    28.         am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pi);  
    29.         am.set(AlarmManager.RTC_WAKEUP, cal2.getTimeInMillis(), pi);  
    30.         am.set(AlarmManager.RTC_WAKEUP, cal3.getTimeInMillis(), pi);  
    31.         am.set(AlarmManager.RTC_WAKEUP, cal4.getTimeInMillis(), pi);  
    32.     }

          上面这部分代码,使用了同一个PendingIntent,设置了4个不同的闹钟时间,这样的效果就是: 只有最后一个闹钟会被触发,前面的所有闹钟都会被忽略掉。


    而要实现多个闹钟的实现是如下:



     

    1. /*
    2.      * Same intent can be scheduled multiple times
    3.      * if you change the request id on the pending intent.
    4.      * Request id identifies an intent as a unique intent.
    5.      */  
    6. public void scheduleDistinctIntents()  
    7.     {  
    8. //Get the instance in time that is   
    9. //30 secs from now.  
    10. 30);  
    11. 35);  
    12. 40);  
    13. 45);  
    14.           
    15. //If you want to point to 11:00 hours today.  
    16. //Calendar cal = Utils.getTodayAt(11);  
    17.   
    18. //Get an intent to invoke  
    19. //TestReceiver class  
    20. new Intent(mContext, TestReceiver.class);  
    21. "message", "Single Shot Alarm");  
    22.   
    23. // Schedule the alarms!  
    24.         AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);  
    25.           
    26. 1));  
    27. 2));  
    28. 3));  
    29. 4));

          每个挂起的Intent,也就是PendingIntent不同,也就是让每个PendingIntent的RequestId不同。产生这种原因是因为在AlarmManager中设置闹钟时,对于相同的Intent,是通过不同的请求Id来创建的。

       在相同类型的Intent上设置闹钟时,只有最后一个闹钟时生效的。

         以下是AlarmMangerService的部分源码,可以看到为什么不能使用相同的PendingIntent了。


    1. public void setRepeating(int type, long triggerAtTime, long interval,   
    2.             PendingIntent operation) {  
    3. .....  
    4. synchronized (mLock) {  
    5. "color:#FF0000;">  Alarm alarm = new Alarm();  
    6.             alarm.type = type;  
    7.             alarm.when = triggerAtTime;  
    8.             alarm.repeatInterval = interval;  
    9.             alarm.operation = operation;</span>  
    10.   
    11. // Remove this alarm if already scheduled.  
    12. "color:#FF0000;">removeLocked(operation);</span>  //当使用相同的operation的时候u,就会先把已有的删除,这样的话,直到最后一个才会生效。  
    13.   
    14. if (localLOGV) Slog.v(TAG, "set: " + alarm);  
    15.   
    16. int index = addAlarmLocked(alarm);  
    17. if (index == 0) {  
    18.                 setLocked(alarm);  
    19.             }  
    20.         }  
    21.     }


    最后一点是关于闹钟的持久化问题,它们是不能保存到设备重新启动之后,也就是说当设备重新启动后,之前设置的闹钟将全部失效。