解读闹钟代码

一.代码位置

二.界面截图

三.类

  • AlarmActivity.java
    是处理弹出提示窗口的一个Activity;

  • AlarmAlertActivity.java
    Main类,设置时间周期等操作

  • AlarmOpreation.java
    核心逻辑处理类,负责计算周期时间,然后将时间通过AlarmManager发送定时广播;

  • AlarmReceiver.java
    广播类,负责处理3发送的广播类型,弹出1;

  • AlarmsSetting.java
    设置的时间信息的存取类;

  • SharedPreferenceUtil.java
    配合5的一个存储类;

  • TimePickerFragment.java
    设置时间的工具类;

  • WeekGridAdpter.java
    设置星期的工具类;

四.顺着去看

核心api:Android原生定时器AlarmManager

  • AlarmActivity点击时间开始
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.switch_in: //上班打卡开关
if (v.isSelected()) {
alarmsSetting.setInEnble(false);
v.setSelected(false);
AlarmOpreation.cancelAlert(AlarmActivity.this, AlarmsSetting.ALARM_SETTING_TYPE_IN);
} else {
alarmsSetting.setInEnble(true);
v.setSelected(true);
AlarmOpreation.enableAlert(AlarmActivity.this, AlarmsSetting.ALARM_SETTING_TYPE_IN, alarmsSetting);
}
break;
case R.id.switch_out://下班打卡开关
if (v.isSelected()) {
alarmsSetting.setOutEnble(false);
v.setSelected(false);
AlarmOpreation.cancelAlert(AlarmActivity.this, AlarmsSetting.ALARM_SETTING_TYPE_OUT);
} else {
alarmsSetting.setOutEnble(true);
v.setSelected(true);
AlarmOpreation.enableAlert(AlarmActivity.this, AlarmsSetting.ALARM_SETTING_TYPE_OUT, alarmsSetting);
}
break;
case R.id.set_in_time: //设置上班时间
showTimePickerDialog(AlarmsSetting.ALARM_SETTING_TYPE_IN);
break;
case R.id.set_out_time://设置下班时间
showTimePickerDialog(AlarmsSetting.ALARM_SETTING_TYPE_OUT);
break;
case R.id.btn_dynamic: //灵活打卡
showSingSelect();
break;
}
}

其中设置时间的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void showTimePickerDialog(final int type) {
TimePickerFragment timePicker = new TimePickerFragment();
if (type == AlarmsSetting.ALARM_SETTING_TYPE_IN) {
timePicker.setTime(alarmsSetting.getInHour(), alarmsSetting.getInMinutes());
} else {
timePicker.setTime(alarmsSetting.getOutHour(), alarmsSetting.getOutMinutes());
}
timePicker.show(getFragmentManager(), "timePicker");
timePicker.setOnSelectListener(new TimePickerFragment.OnSelectListener() {
@Override
public void getValue(int hourOfDay, int minute) {
setTime1(type,hourOfDay,minute);
}
});
}
  • TimePickerFragment窗口
    此类实质上就是继承至 DialogFragment调用 TimePickerDialog向外提供获取小时和分钟的接口!
  • WeekGridAdpter.java
    而星期的周期复杂些,此行星期选项列表是一排 GridView,WeekGridAdpter就是它的 Adapter,在构造方法的 GetView中,可以看出,大神将周一至周日,组成一个二进制数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if(v.isSelected()){
selected = selected - (int)(1 << position);
if(selected <= 0) {//至少选一个
selected = selected + (int)(1 << position);
return ;
}
v.setSelected(false);
}else{
selected = selected + (int)(1 << position);
v.setSelected(true);
}
if(type==AlarmsSetting.ALARM_SETTING_TYPE_IN){
alarmsSetting.setInDays(selected);
}else if(type==AlarmsSetting.ALARM_SETTING_TYPE_OUT){
alarmsSetting.setOutDays(selected);
}

传入对应参数到AlarmOpreation

1
2
AlarmOpreation.cancelAlert(context,type);
AlarmOpreation.enableAlert(context, type, alarmsSetting);
  • AlarmOpreation.class
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
释放
**/
public static void cancelAlert(Context context, int type) {
AlarmManager mAlarmManager = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(AlarmsSetting.ALARM_ALERT_ACTION);
intent.putExtra("type", type);
intent.setClass(context, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, type, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
mAlarmManager.cancel(pi);
}

设置:主要是将存储好的时间设置信息(小时,分钟,星期),通过 cacluteNextAlarm方法设置成一个特殊的 Calendar值用于定时,然后将对应的 type和 Action组成一个通过广播 pi!通过 AlarmManager的 set方法定时, mAlarmManager.set(AlarmManager.RTC_WAKEUP,mCalendar.getTimeInMillis(),pi);,定时将 pi中的内容发送出去!
核心就是这样!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
设置
**/
public static void enableAlert(Context context, int type, AlarmsSetting alarmsSetting) {
if(type==AlarmsSetting.ALARM_SETTING_TYPE_IN && !alarmsSetting.isInEnble()){
return ;
}else if(type==AlarmsSetting.ALARM_SETTING_TYPE_OUT && !alarmsSetting.isOutEnble()){
return;
}
int rantime = alarmsSetting.getDynamic();
AlarmManager mAlarmManager = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
int hours = 0,minute=0,dayOfweek=0;
if(type==AlarmsSetting.ALARM_SETTING_TYPE_IN){
hours = alarmsSetting.getInHour();
minute=alarmsSetting.getInMinutes();
dayOfweek = alarmsSetting.getInDays();
}else if(type==AlarmsSetting.ALARM_SETTING_TYPE_OUT){
hours = alarmsSetting.getOutHour();
minute=alarmsSetting.getOutMinutes();
dayOfweek=alarmsSetting.getOutDays();
}
...
}

处理成循环周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public static Calendar cacluteNextAlarm(int hour, int minute, int dayOfweek){
Calendar mCalendar = Calendar.getInstance();
mCalendar.setTimeInMillis(System.currentTimeMillis());
mCalendar.set(Calendar.HOUR_OF_DAY,hour);
mCalendar.set(Calendar.MINUTE, minute);
int differDays = getNextAlarmDifferDays(dayOfweek,mCalendar.get(Calendar.DAY_OF_WEEK), mCalendar.getTimeInMillis());
int nextYear = getNextAlarmYear(mCalendar.get(Calendar.YEAR), mCalendar.get(Calendar.DAY_OF_YEAR), mCalendar.getActualMaximum(Calendar.DAY_OF_YEAR), differDays);
int nextMonth = getNextAlarmMonth(mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DAY_OF_MONTH), mCalendar.getActualMaximum(Calendar.DATE), differDays);
int nextDay = getNextAlarmDay(mCalendar.get(Calendar.DAY_OF_MONTH), mCalendar.getActualMaximum(Calendar.DATE), differDays);
mCalendar.set(Calendar.YEAR,nextYear);
mCalendar.set(Calendar.MONTH, nextMonth % 12);//月份从0开始
mCalendar.set(Calendar.DAY_OF_MONTH, nextDay);
mCalendar.set(Calendar.SECOND, 0);
mCalendar.set(Calendar.MILLISECOND, 0);
return mCalendar;
}


//获取下次闹钟相差的天数
private static int getNextAlarmDifferDays(int data, int currentDayOfWeek,long timeInMills){
int nextDayOfWeek = getNextDayOfWeek(data, currentDayOfWeek,timeInMills);
return currentDayOfWeek<=nextDayOfWeek?(nextDayOfWeek-currentDayOfWeek):(7 - currentDayOfWeek + nextDayOfWeek);
}


//考虑年进位的情况
private static int getNextAlarmYear(int year,int dayOfYears, int actualMaximum, int differDays) {
int temp = actualMaximum-dayOfYears-differDays;
return temp >= 0?year:year+1;
}

//考虑月进位的情况
private static int getNextAlarmMonth(int month,int dayOfMonth,int actualMaximum, int differDays) {
int temp = actualMaximum-dayOfMonth-differDays;
return temp >= 0?month:month+1;
}

//获取下次闹钟的day
private static int getNextAlarmDay(int thisDayOfMonth, int actualMaximum, int differDays) {
int temp = actualMaximum - thisDayOfMonth-differDays;
if (temp<0){
return thisDayOfMonth + differDays - actualMaximum;
}
return thisDayOfMonth + differDays;
}

//获取下次显示是星期几
private static int getNextDayOfWeek(int data, int cWeek,long timeInMillis) {
int tempBack = data >> cWeek - 1;
int tempFront = data ;

if(tempBack%2==1){
if(System.currentTimeMillis()<timeInMillis) return cWeek;
}
tempBack = tempBack>>1;
int m=1,n=0;
while (tempBack != 0) {
if (tempBack % 2 == 1 ) return cWeek + m;
tempBack = tempBack / 2;
m++;
}
while(n<cWeek){
if (tempFront % 2 == 1) return n+1;
tempFront =tempFront/2;
n++;
}
return 0;
}

然后通过SharedPreferenceUtil将设置的值存储下来

当设置好定时器后,系统就会在指定的时间发送广播过来通知

  • AlarmReceiver.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Override
public void onReceive(Context context, Intent intent) {
alarmsSetting = new AlarmsSetting(context);
int type = intent.getIntExtra("type",0);
// Log.e("#######################", "getRecevier_ACtion" + intent.getAction());

//如果已经设置闹钟w不可用,先拦截
if(type==AlarmsSetting.ALARM_SETTING_TYPE_IN && !alarmsSetting.isInEnble()){
return ;
}else if(type==AlarmsSetting.ALARM_SETTING_TYPE_OUT && !alarmsSetting.isOutEnble()){
return;
}

if(intent.getAction().equals(AlarmsSetting.ALARM_ALERT_ACTION) && type !=0) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日——HH时mm分ss秒SSS毫秒");
Log.e("###########此次闹钟#######", "alarmsSetting.getNextAlarm()" + formatter.format(new Date(alarmsSetting.getNextAlarm())));
Log.e("###########当前系统时间###", "System.currentTimeMillis()" + formatter.format(new Date(System.currentTimeMillis())));
if (alarmsSetting.getNextAlarm() + 1000 * 30 < System.currentTimeMillis()){//解决闹钟广播比设置时间闹钟快的问题
Log.e("###########无效闹钟#######", "不执行");
return;
}
Log.e("###########准备弹出提示框###", " ");
intent.setClass(context, AlarmAlertActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
AlarmOpreation.cancelAlert(context, type);
AlarmOpreation.enableAlert(context, type, new AlarmsSetting(context));
}else{
AlarmOpreation.cancelAlert(context, AlarmsSetting.ALARM_SETTING_TYPE_IN);
AlarmOpreation.enableAlert(context, AlarmsSetting.ALARM_SETTING_TYPE_IN, new AlarmsSetting(context));
AlarmOpreation.cancelAlert(context, AlarmsSetting.ALARM_SETTING_TYPE_OUT);
AlarmOpreation.enableAlert(context, AlarmsSetting.ALARM_SETTING_TYPE_OUT, new AlarmsSetting(context));
}
}