毕业设计中有个功能模块叫就医提醒,大抵的功能就是用户设定一个未来时间的闹钟,并设置闹钟的标签,标签上写着去哪里就医之类的信息,主要设计参考魅族系统自带的闹钟功能。我在网上看了不少博客,也在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即可实现整个的逻辑。
因我的代码借鉴了他人的代码,导致我在代码的阅读中陷入了极大的混乱,同时整个的逻辑也非常的不清晰,造成了代码工作非常缓慢的事实,此后我必须在阅读他人的代码的基础上理解整个逻辑实现,理清思路,完成自己的设计。