Android学习笔记之AlarmManager有关的定时器和闹钟的实现

时间:2022-12-26 10:51:34

  毕业设计中有个功能模块叫就医提醒,大抵的功能就是用户设定一个未来时间的闹钟,并设置闹钟的标签,标签上写着去哪里就医之类的信息,主要设计参考魅族系统自带的闹钟功能。我在网上看了不少博客,也在github上下载了不少源码,发现也没有写的特别好的,总有这种或者那种的问题,比如说闹钟不是写成后台服务的模式,APP关闭之后闹钟不会提醒,或者手机关机之后之前设置的闹钟信息丢失,所以我准备自己写一个功能比较齐全的。

  所以这个闹钟的功能需要用到Android的数据库Sqlite、Service、BroadcastReceiver三大比较重要的模块,之前在看《第一行代码》时,由于看的比较匆忙,所以书本后面的部分知识点学习的并不扎实,书中的代码并没有亲自在IDE中敲一敲,所以借着五一假期三天又重头好好看了一遍《第一行代码》中关于数据库Sqlite、Service、BroadcastReceiver的部分。

  Sqlite是一个轻量级的Android内置数据库,通常只占几百KB的内存,所以在移动设备上也非常的适用。创建数据库的表如下,并写了一个Helper类对数据库操作进行了封装,简化了底层操作数据的操作。

 1 public class AlarmDBHelper extends SQLiteOpenHelper{
 2     public static final String DATABASE_NAME = "userinfo.db";
 3     public static final int DATABASE_VERSION = 2;
 4     public static final String TABLE = "alarm";
 5 
 6 
 7     private static final String CREATE_ALARM_TABLE = "CREATE TABLE " + TABLE + " ("
 8             + AlarmColumn._ID + " integer primary key AUTOINCREMENT,"
 9             + AlarmColumn.ALARM_ID + " text UNIQUE ON CONFLICT REPLACE,"
10             + AlarmColumn.ALARM_CALENDAR + " text,"
11             + AlarmColumn.ALARM_CANCELABLE + " text,"
12             + AlarmColumn.ALARM_TAG + " text,"
13             + AlarmColumn.ALARM_AVAILABLE + " text,"
14             + AlarmColumn.ALARM_RINGTONE + " text)";
15 
16     public AlarmDBHelper(Context context) {
17         super(context, DATABASE_NAME, null, DATABASE_VERSION);
18     }
19 
20     @Override
21     public void onCreate(SQLiteDatabase db) {
22         db.execSQL(CREATE_ALARM_TABLE);
23     }
24 
25     @Override
26     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
27         String sql=" DROP TABLE IF EXISTS "+TABLE;
28         db.execSQL(sql);
29         onCreate(db);
30     }
31 }

若要修改原先的数据库,只需要在 dbHelper = new AlarmDBHelper(this, "userinfo.db", null, 3); 中增加version的版本号即可。数据库的操作无非就是CRUD,C代表添加(Create),R代表查询(Retrieve),U代表更新(Update),D代表删除(Delete)。操作基本与SQL相同,同时官方支持使用SQL语句建立数据库和CRUD。其中查询数据库设计到一个新的数据类型——Cursor,本意为光标,可以理解为数据库中每行的内容。cursor.moveToFirst()移动到表中的第一行,cursor.moveToNext() 即指移动到下一行。上述代码即创建一个闹钟的 table ,将创建闹钟所需的一些信息保存在数据库中。

 1 public class BootReceiver extends BroadcastReceiver{
 2     @Override
 3     public void onReceive(Context context, Intent intent) {
 4         ArrayList<Alarm> alarms = getAllAlarms(context);
 5         for (Alarm alarm : alarms) {
 6             // Test
 7             // 当重启后,所有的都应该恢复,而如果这是定时任务,那么只要恢复月度和年度的就可以了.
 8             // 如果每天00:00重建闹钟的话,那么00:00时响的闹钟会不会响呢
 9             // 所以最后错开一点,因为闹钟没有秒数,所以设置为00:00:30秒何如。
10             // 不论是什么闹钟,都会保证如果第二天有闹钟的话就会设置上的,所以不用担心00:00的闹钟不会设置上
11             alarm.activate();
12         }
13     }
14 
15     /**
16      * 从本地数据库恢复所有的闹钟
17      *
18      * @return
19      */
20     private ArrayList<Alarm> getAllAlarms(Context context) {
21         AlarmHelper helper = new AlarmHelper(context);
22         return helper.getAlarms();
23     }
24 }

并注册一个广播监听开机的action,每当开机时将所有的alarm从数据库中都出来,并激活所有的闹钟,即AlarmReceive监听所有闹钟。

 1  private void setOneTimeAlarm() {
 2         if (AudreyCalendar.getTimeInMillis() - System.currentTimeMillis() > 0) {
 3             // 最后一个参数必须是PendingIntent.FLAG_UPDATE_CURRENT,否则BroadcastReceiver将收不到参数。
 4             PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
 5                     0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
 6             AlarmManager manager = (AlarmManager) mContext
 7                     .getSystemService(Context.ALARM_SERVICE);
 8             manager.set(AlarmManager.RTC_WAKEUP,
 9                     AudreyCalendar.getTimeInMillis(), pendingIntent);
10         } else {
11             Toast.makeText(mContext, "小伙子,设置太早闹钟是不会执行滴!",
12                     Toast.LENGTH_SHORT).show();
13         }
14     }

通过这样数据库中的闹钟会在 setOneTimeAlarm() 设置响铃的时间。AlarmReceive 中的代码如下,

 1 public class AlarmReceiver extends BroadcastReceiver{
 2 
 3     @Override
 4     public void onReceive(Context context, Intent intent) {
 5 
 6         Alarm alarm=new Alarm(context, intent.getExtras());
 7         Intent intent2=new Intent();
 8         intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 9         intent2.setClass(context, RingActivity.class);
10         intent2.putExtras(Alarm.alarm2Bundle(alarm));
11         context.startActivity(intent2);
12 
13 
14 
15     }
16 }

通过Receive中的方法启动RingActivity,触发响铃的操作,整个闹钟的逻辑就完成了。

按理说,闹钟的实现应该通过service实现,这样就可以使得即使app关闭时,后台的闹钟也是在运行的,不过如何在service中启动一个闹钟我不知道如何实现。整个的逻辑应该是当设置好闹钟的属性,点击增加闹钟时,闹钟的信息被保存在数据库中,同时触发一个service启动,在service服务中接受alarm中的信息,设置后一个

 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
 5                     0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 6 AlarmManager manager = (AlarmManager) mContext 7  .getSystemService(Context.ALARM_SERVICE); 8  manager.set(AlarmManager.RTC_WAKEUP, 9  AudreyCalendar.getTimeInMillis(), pendingIntent); 将getBroadcast改成getActivity,跳转到RingActivity即可实现整个的逻辑。


因我的代码借鉴了他人的代码,导致我在代码的阅读中陷入了极大的混乱,同时整个的逻辑也非常的不清晰,造成了代码工作非常缓慢的事实,此后我必须在阅读他人的代码的基础上理解整个逻辑实现,理清思路,完成自己的设计。