多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開(kāi)源 > 綜合技術(shù) > Android時(shí)鐘應(yīng)用的定時(shí)框架分析

Android時(shí)鐘應(yīng)用的定時(shí)框架分析

來(lái)源:程序員人生   發(fā)布時(shí)間:2014-10-10 08:00:00 閱讀次數(shù):3369次

Android系統(tǒng)鬧鐘定時(shí)功能框架,總體來(lái)說(shuō)就是用數(shù)據(jù)庫(kù)存儲(chǔ)定時(shí)數(shù)據(jù),有一個(gè)狀態(tài)管理器來(lái)統(tǒng)一管理這些定時(shí)狀態(tài)的觸發(fā)和更新。在Andriod系統(tǒng)中實(shí)現(xiàn)定時(shí)功能,最終還是要用到系統(tǒng)提供的AlarmManager,只是當(dāng)一個(gè)定時(shí)完成后怎么繼續(xù)處理,或者中間怎么更新定時(shí)的時(shí)間或者狀態(tài),像鬧鐘這種應(yīng)用程序,每天重復(fù)定時(shí),或者一周選擇其中的幾天,鬧鐘響了延遲5分鐘再次響鈴,這時(shí)候就需要想一種好的辦法來(lái)讓管理這些數(shù)據(jù)和狀態(tài),下面就分析一下Android系統(tǒng)鬧鐘的實(shí)現(xiàn)。


1、基本結(jié)構(gòu)


Alarm

代表一條定時(shí)數(shù)據(jù)

AlarmInstance

代表一個(gè)定時(shí)項(xiàng)目的實(shí)例,一個(gè)AlarmInstance對(duì)應(yīng)到一個(gè)Alarm,相比Alarm多存儲(chǔ)了一些狀態(tài)信息

AlarmStateManager

狀態(tài)管理器,對(duì)定時(shí)項(xiàng)目進(jìn)行調(diào)度,添加、刪除、更改狀態(tài),是一個(gè)BroadcastReciever,定時(shí)到點(diǎn)后發(fā)廣播到這里進(jìn)行下一步處理

AlarmService

響應(yīng)結(jié)果,也就是定時(shí)到達(dá)后要做的事,響鈴,停止響鈴

ClockDataHelper

里面創(chuàng)建了三個(gè)表,ALARMS_TABLE,INSTANCE_TABLE,CITIES_TABLE,前兩個(gè)分別對(duì)應(yīng)到上面的Alarm和AlarmInstance。

private static void createAlarmsTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + ALARMS_TABLE_NAME + " (" + ClockContract.AlarmsColumns._ID + " INTEGER PRIMARY KEY," + ClockContract.AlarmsColumns.HOUR + " INTEGER NOT NULL, " + ClockContract.AlarmsColumns.MINUTES + " INTEGER NOT NULL, " + ClockContract.AlarmsColumns.DAYS_OF_WEEK + " INTEGER NOT NULL, " + ClockContract.AlarmsColumns.ENABLED + " INTEGER NOT NULL, " + ClockContract.AlarmsColumns.VIBRATE + " INTEGER NOT NULL, " + ClockContract.AlarmsColumns.LABEL + " TEXT NOT NULL, " + ClockContract.AlarmsColumns.RINGTONE + " TEXT, " + ClockContract.AlarmsColumns.DELETE_AFTER_USE + " INTEGER NOT NULL DEFAULT 0);"); Log.i("Alarms Table created"); }
private static void createInstanceTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + INSTANCES_TABLE_NAME + " (" + ClockContract.InstancesColumns._ID + " INTEGER PRIMARY KEY," + ClockContract.InstancesColumns.YEAR + " INTEGER NOT NULL, " + ClockContract.InstancesColumns.MONTH + " INTEGER NOT NULL, " + ClockContract.InstancesColumns.DAY + " INTEGER NOT NULL, " + ClockContract.InstancesColumns.HOUR + " INTEGER NOT NULL, " + ClockContract.InstancesColumns.MINUTES + " INTEGER NOT NULL, " + ClockContract.InstancesColumns.VIBRATE + " INTEGER NOT NULL, " + ClockContract.InstancesColumns.LABEL + " TEXT NOT NULL, " + ClockContract.InstancesColumns.RINGTONE + " TEXT, " + ClockContract.InstancesColumns.ALARM_STATE + " INTEGER NOT NULL, " + ClockContract.InstancesColumns.ALARM_ID + " INTEGER REFERENCES " + ALARMS_TABLE_NAME + "(" + ClockContract.AlarmsColumns._ID + ") " + "ON UPDATE CASCADE ON DELETE CASCADE" + ");"); Log.i("Instance table created"); }

這里說(shuō)一下幾個(gè)特殊的字段,對(duì)于Alarm表,DAYS_OF_WEEK表示一周內(nèi)需要定時(shí)的天(鬧鐘有個(gè)功能是選擇一周中的幾天),這里是個(gè)int值,用位來(lái)表示設(shè)置的天數(shù),源碼中有個(gè)專(zhuān)門(mén)的類(lèi)DaysOfWeek來(lái)存儲(chǔ)和處理。

AlarmInstance表中有一個(gè)ALARM_ID,關(guān)聯(lián)到一個(gè)Alarm,可以看到在AlarmInstance表里也有時(shí)間,為什么不和Alarm表合成一個(gè)表?應(yīng)該是這樣的,Alarm表示原始的定時(shí)項(xiàng),是一個(gè)基礎(chǔ)數(shù)據(jù),而AlarmInstance則代表了一個(gè)使用中的定時(shí)項(xiàng)目,或者是一個(gè)已經(jīng)激活的定時(shí)項(xiàng)目,它的時(shí)間是可以變化的,比如鬧鐘響了以后延時(shí)5分鐘再響,就需要改變這里的時(shí)間,而基礎(chǔ)數(shù)據(jù)不能變,還需要顯示在那里。ALARM_STATE代表了當(dāng)前定時(shí)項(xiàng)目的狀態(tài),具體調(diào)度都在AlarmStateManager中管理。

忘了在哪里看到的,“編程最重要的是設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu),接下來(lái)是分解各種代碼塊”。數(shù)據(jù)結(jié)構(gòu)是基礎(chǔ),就像建筑里的鋼筋水泥磚瓦,有了基礎(chǔ)的材料后,剩下的工作就是對(duì)這些材料處理,也就是設(shè)計(jì)具體的處理邏輯。


2、具體的類(lèi)分析


Alarm


從上面也可以看出,Alarm類(lèi)作為定時(shí)的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),主要是封裝了一些數(shù)據(jù)庫(kù)操作,完成增刪改查功能。額外有一個(gè)方法createInstanceAfter,根據(jù)自身來(lái)創(chuàng)建一個(gè)AlarmInstance實(shí)例。代碼如下

public AlarmInstance createInstanceAfter(Calendar time) { Calendar nextInstanceTime = Calendar.getInstance(); nextInstanceTime.set(Calendar.YEAR, time.get(Calendar.YEAR)); nextInstanceTime.set(Calendar.MONTH, time.get(Calendar.MONTH)); nextInstanceTime.set(Calendar.DAY_OF_MONTH, time.get(Calendar.DAY_OF_MONTH)); nextInstanceTime.set(Calendar.HOUR_OF_DAY, hour); nextInstanceTime.set(Calendar.MINUTE, minutes); nextInstanceTime.set(Calendar.SECOND, 0); nextInstanceTime.set(Calendar.MILLISECOND, 0); // If we are still behind the passed in time, then add a day if (nextInstanceTime.getTimeInMillis() <= time.getTimeInMillis()) { nextInstanceTime.add(Calendar.DAY_OF_YEAR, 1); } // The day of the week might be invalid, so find next valid one int addDays = daysOfWeek.calculateDaysToNextAlarm(nextInstanceTime); if (addDays > 0) { nextInstanceTime.add(Calendar.DAY_OF_WEEK, addDays); } AlarmInstance result = new AlarmInstance(nextInstanceTime, id); result.mVibrate = vibrate; result.mLabel = label; result.mRingtone = alert; return result; }

AlarmInstance

AlarmInstance與Alarm很相似,像Alarm中的增刪改查操作在AlarmInstance中都有相似的方法。那有什么不同呢,就是上面說(shuō)的AlarmInstance的時(shí)間是可以根據(jù)當(dāng)前狀態(tài)改變的,也就多了時(shí)間的set和get方法。

public void setAlarmTime(Calendar calendar) { mYear = calendar.get(Calendar.YEAR); mMonth = calendar.get(Calendar.MONTH); mDay = calendar.get(Calendar.DAY_OF_MONTH); mHour = calendar.get(Calendar.HOUR_OF_DAY); mMinute = calendar.get(Calendar.MINUTE); } /** * Return the time when a alarm should fire. * * @return the time */ public Calendar getAlarmTime() { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.YEAR, mYear); calendar.set(Calendar.MONTH, mMonth); calendar.set(Calendar.DAY_OF_MONTH, mDay); calendar.set(Calendar.HOUR_OF_DAY, mHour); calendar.set(Calendar.MINUTE, mMinute); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar; }

AlarmStateManager

鬧鐘定時(shí)的核心邏輯就在這里,AlarmStateManager就是管理所有定時(shí)項(xiàng)目狀態(tài)的調(diào)度器。



可以看到上面大多是static類(lèi)型的方法,用于設(shè)置各種狀態(tài)值。

先看一下定時(shí)的幾種狀態(tài):

SILENT_STATE,alarm被激活,但是不需要顯示任何東西,下一個(gè)狀態(tài)是LOW_NOTIFICATION_STATE;

LOW_NOTIFICATION_STATE,這個(gè)狀態(tài)表示alarm離觸發(fā)的時(shí)間不遠(yuǎn)了,時(shí)間差是AlarmInstance.LOW_NOTIFICATION_HOUR_OFFSET=-2,也就是2個(gè)小時(shí)。下一個(gè)狀態(tài)會(huì)進(jìn)入HIGH_NOTIFICATION_STATE,HIDE_NOTIFICATION_STATE,DISMISS_STATE;

HIDE_NOTIFICATION_STATE,這是一個(gè)暫時(shí)態(tài),表示用戶(hù)想隱藏掉通知,這個(gè)狀態(tài)會(huì)一直持續(xù)到HIGH_NOTIFICATION_STATE;

HIGH_NOTIFICATION_STATE,這個(gè)狀態(tài)和LOW_NOTIFICATION_STATE相似,但不允許用戶(hù)隱藏通知,負(fù)責(zé)觸發(fā)FIRED_STATE或者DISMISS_STATE;

SNOOZED_STATE,像HIGH_NOTIFICATION_STATE,但是會(huì)增加一點(diǎn)定時(shí)的時(shí)間來(lái)完成延遲功能;

FIRED_STATE,表示響鈴狀態(tài),會(huì)啟動(dòng)AlarmService直到用戶(hù)將其變?yōu)镾NOOZED_STATE或者DISMISS_STATE,如果用戶(hù)放任不管,會(huì)之后進(jìn)入MISSED_STATE;

MISSED_STATE,這個(gè)狀態(tài)在FIRED_STATE之后,會(huì)在通知欄給出一個(gè)提醒剛才響鈴了;

DISMISS_STATE,這個(gè)狀態(tài)表示定時(shí)結(jié)束了,會(huì)根據(jù)定時(shí)項(xiàng)目的設(shè)置判斷是否需要重復(fù),從而決定要?jiǎng)h除這個(gè)項(xiàng)目還是繼續(xù)設(shè)定一個(gè)新的定時(shí)。

上面的 setXXXState 方法就是對(duì)這些狀態(tài)的處理,同時(shí)會(huì)規(guī)劃一個(gè)定時(shí)轉(zhuǎn)換到下一個(gè)狀態(tài)。比如setSilentState:

public static void setSilentState(Context context, AlarmInstance instance) { Log.v("Setting silent state to instance " + instance.mId); // Update alarm in db ContentResolver contentResolver = context.getContentResolver(); instance.mAlarmState = AlarmInstance.SILENT_STATE; AlarmInstance.updateInstance(contentResolver, instance); // Setup instance notification and scheduling timers AlarmNotifications.clearNotification(context, instance); scheduleInstanceStateChange(context, instance.getLowNotificationTime(), instance, AlarmInstance.LOW_NOTIFICATION_STATE); }

更新AlarmInstance的信息,同時(shí)通過(guò)scheduleInstanceStateChange()規(guī)劃下一個(gè)狀態(tài):

private static void scheduleInstanceStateChange(Context context, Calendar time, AlarmInstance instance, int newState) { long timeInMillis = time.getTimeInMillis(); Log.v("Scheduling state change " + newState + " to instance " + instance.mId + " at " + AlarmUtils.getFormattedTime(context, time) + " (" + timeInMillis + ")"); Intent stateChangeIntent = createStateChangeIntent(context, ALARM_MANAGER_TAG, instance, newState); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, instance.hashCode(), stateChangeIntent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); if (Utils.isKitKatOrLater()) { am.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent); } else { am.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent); } }

通過(guò)AlarmManager發(fā)起一個(gè)定時(shí),定時(shí)的時(shí)間從調(diào)用處可以看到是有AlarmInstance得到的,比如在setSilentState()中的定時(shí)時(shí)間是instance.getLowNotificationTime():

public Calendar getLowNotificationTime() { Calendar calendar = getAlarmTime(); calendar.add(Calendar.HOUR_OF_DAY, LOW_NOTIFICATION_HOUR_OFFSET); return calendar; }

LOW_NOTIFICATION_HOUR_OFFSET值為-2,也就是在鬧鈴響之前的兩小時(shí)那一刻會(huì)發(fā)這個(gè)LOW_NOTIFICATION_STATE的廣播出來(lái),AlarmStateManager接收到這個(gè)廣播處理再轉(zhuǎn)移到下一個(gè)。廣播的接收在onReciever方法中,

@Override public void onReceive(final Context context, final Intent intent) { final PendingResult result = goAsync(); final PowerManager.WakeLock wl = AlarmAlertWakeLock.createPartialWakeLock(context); wl.acquire(); AsyncHandler.post(new Runnable() { @Override public void run() { handleIntent(context, intent); result.finish(); wl.release(); } }); } private void handleIntent(Context context, Intent intent) { final String action = intent.getAction(); Log.v("AlarmStateManager received intent " + intent); if (CHANGE_STATE_ACTION.equals(action)) { Uri uri = intent.getData(); AlarmInstance instance = AlarmInstance.getInstance(context.getContentResolver(), AlarmInstance.getId(uri)); if (instance == null) { // Not a big deal, but it shouldn't happen Log.e("Can not change state for unknown instance: " + uri); return; } int globalId = getGlobalIntentId(context); int intentId = intent.getIntExtra(ALARM_GLOBAL_ID_EXTRA, -1); int alarmState = intent.getIntExtra(ALARM_STATE_EXTRA, -1); if (intentId != globalId) { Log.i("Ignoring old Intent. IntentId: " + intentId + " GlobalId: " + globalId + " AlarmState: " + alarmState); return; } if (alarmState >= 0) { setAlarmState(context, instance, alarmState); } else { registerInstance(context, instance, true); } } else if (SHOW_AND_DISMISS_ALARM_ACTION.equals(action)) { Uri uri = intent.getData(); AlarmInstance instance = AlarmInstance.getInstance(context.getContentResolver(), AlarmInstance.getId(uri)); long alarmId = instance.mAlarmId == null ? Alarm.INVALID_ID : instance.mAlarmId; Intent viewAlarmIntent = Alarm.createIntent(context, DeskClock.class, alarmId); viewAlarmIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX); viewAlarmIntent.putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, alarmId); viewAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(viewAlarmIntent); setDismissState(context, instance); } } }

在handleIntent方法中統(tǒng)一處理,狀態(tài)的分發(fā)在setAlarmState中:

public void setAlarmState(Context context, AlarmInstance instance, int state) { switch(state) { case AlarmInstance.SILENT_STATE: setSilentState(context, instance); break; case AlarmInstance.LOW_NOTIFICATION_STATE: setLowNotificationState(context, instance); break; case AlarmInstance.HIDE_NOTIFICATION_STATE: setHideNotificationState(context, instance); break; case AlarmInstance.HIGH_NOTIFICATION_STATE: setHighNotificationState(context, instance); break; case AlarmInstance.FIRED_STATE: setFiredState(context, instance); break; case AlarmInstance.SNOOZE_STATE: setSnoozeState(context, instance); break; case AlarmInstance.MISSED_STATE: setMissedState(context, instance); break; case AlarmInstance.DISMISSED_STATE: setDismissState(context, instance); break; default: Log.e("Trying to change to unknown alarm state: " + state); } }

對(duì)沒(méi)一個(gè)state又轉(zhuǎn)移相應(yīng)的setXXXState方法中,完成下一次狀態(tài)的轉(zhuǎn)換,形成一個(gè)定時(shí)的循環(huán),直到在DISMISSED_STATE里停用或者刪除定時(shí)項(xiàng)目,如果需要重復(fù)則獲取下一次定時(shí)的時(shí)間。

整體的框架就是這樣,在AlarmStateManager里使用AlarmManager形成了一個(gè)定時(shí)的狀態(tài)機(jī),不斷轉(zhuǎn)移到下一個(gè)狀態(tài)處理。

源碼在這里https://android.googlesource.com/platform/packages/apps/DeskClock/+/android-4.4.4_r2.0.1

生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 亚洲综合一区二区精品久久 | 五月天综合 | 国产成人精品一区二三区 | 91精品一区国产高清在线 | 国产精品久久久久久久久 | 在线观看网址 | 亚拍精品一区二区三区 | 在线一区二区三区 | 国产一区在线视频 | 国产亚洲精品一区二区在线观看 | 国产一区欧美 | 日本免费性 | 午夜视频在线免费播放 | 亚洲天堂资源网 | 国产一成人精品福利网站 | 成人在线免费视频 | www.性欧美| 亚洲福利视频一区二区三区 | 亚洲另类图片专区 | 亚洲天堂资源网 | 亚洲第一网站在线观看 | 久久91综合国产91久久精品 | 自拍偷拍欧美亚洲 | 亚洲精品永久免费 | 成人毛片国产a | 亚洲视频在线不卡 | 国产亚洲精品自在线观看 | xxfree性欧美hd | 欧美精品一区二区三区在线 | 午夜影院免费入口 | 国产精品一区高清在线观看 | 国产成人免费片在线观看 | 精品无码久久久久久国产 | 亚洲第一黄色网 | 国产a网| 一级毛片a女人刺激视频免费 | 免费片子 | 国产高清一区二区三区视频 | 性xxxxfreexxxxx欧美吹潮 | 麻豆影视在线最新免费观看 | 午夜精品久久久久久中宇 |