Timing framework analysis for Android clock applications

Source: Internet
Author: User
Tags repetition

The Android System alarm timer function framework, in general, is to use the database to store timing data, there is a state manager to manage these timing status of the trigger and update. In the andriod system to achieve the timing function, the end is to use the system provided by the Alarmmanager, just when a timed completion of how to continue processing, or the middle how to update the timing or state, such as the alarm clock such an application, the daily repetition, or a week to choose the days, Alarm clock rang 5 minutes delay again, this time you need to think of a good way to manage these data and status, the following analysis of the Android system alarm clock implementation.


1. Basic structure
Alarm

Represents a timed data

Alarminstance

Represents an instance of a timed project, a alarminstance corresponds to a alarm, and some state information is stored more than alarm

Alarmstatemanager

State Manager, scheduling the scheduled items, adding, deleting, changing the state, is a broadcastreciever, timed to the point after the broadcast to the next process here

Alarmservice

The result of the response, the thing to do after a timed arrival, a bell, a stop bell

Clockdatahelper

It created three tables, alarms_table,instance_table,cities_table, and the first two correspond to the above alarm and alarminstance respectively.

 private                static void Createalarmstable (Sqlitedatabase db) {db.execsql ("CREATE TABLE" + Alarms_table_name + "(" + clockcontract.alarmscolumns._id + "INTEGER PRIMARY KEY," + ClockContract.AlarmsColumns.HOUR + "INT EGER NOT NULL, ' + ClockContract.AlarmsColumns.MINUTES + ' INTEGER not null, ' + Clockcontra Ct. Alarmscolumns.days_of_week + "integer Not NULL," + ClockContract.AlarmsColumns.ENABLED + "integer Not NU LL, "+ ClockContract.AlarmsColumns.VIBRATE +" INTEGER not NULL, "+ Clockcontract.alarmsco Lumns. LABEL + "text not NULL," + ClockContract.AlarmsColumns.RINGTONE + "text," + Clockcontrac        T.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.instan                Cescolumns.year + "integer NOT NULL," + ClockContract.InstancesColumns.MONTH + "integer NOT NULL," + ClockContract.InstancesColumns.DAY + "INTEGER not NULL," + ClockContract.InstancesColumns.HO                UR + "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.inst Ancescolumns.alarm_state + "integer Not NULL," + ClockContract.InstancesColumns.ALARM_ID + "integer Refe              Rences "+      Alarms_table_name + "(" + clockcontract.alarmscolumns._id + ")" + "on UPDATE CASCADE on DELETE C        Ascade "+"); ");    LOG.I ("Instance table created"); }

Here are a few special fields, for the alarm table, Days_of_week represents the day of the week need to be timed (alarm clock has a function is to select a few days of the week), here is an int value, with a bit to indicate the number of days set, the source has a special class DaysOfWeek to store and processing.

Alarminstance table has a alarm_id, associated to a alarm, you can see in the Alarminstance table also has time, why not and alarm tables to synthesize a table? This should be the case, alarm represents the original timing item, is a basic data, and alarminstance represents a use of the timing item, or an activated timing item, its time can be changed, such as the alarm clock rang after 5 minutes delay, and then need to change the time here , and the underlying data does not change and needs to be shown there. Alarm_state represents the status of the current timed items, and the specific schedules are managed in Alarmstatemanager.

Forget where to see, "the most important thing in programming is designing data structures, and then breaking down various blocks of code." The data structure is the basis, like the building of the steel cement bricks and mortar, with the basic material, the rest of the work is to deal with these materials, that is, the design of specific processing logic.


2, the specific class analysis
Alarm


As can be seen from the above, alarm class as the basic data structure of the timing, mainly encapsulates some database operations, complete the function of adding and censoring. There is an additional method Createinstanceafter, which creates a alarminstance instance based on itself. The code is as follows

    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 and then add a day if (Nextinstancetime.gettimeinmillis () <= Time.gett        Imeinmillis ()) {Nextinstancetime.add (calendar.day_of_year, 1); }//The day of the week might is invalid, so find next valid one int addDays = Daysofweek.calculatedaystone        Xtalarm (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 and alarm very similar, such as alarm in the deletion and modification of the operation in the Alarminstance have similar methods. What is the difference, that is, the alarminstance time can be changed according to the current state, but also more time set and get method.

    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

The core logic of the alarm clock is here, Alarmstatemanager is the scheduler that manages the state of all timed items.



You can see that most of the static types above are used to set various status values.

Let's take a look at some of the timing states:

Silent_state,alarm is activated, but does not need to show anything, the next state is low_notification_state;

Low_notification_state, this state indicates that the alarm is not far from the trigger, the time difference is alarminstance.low_notification_hour_offset=-2, that is, 2 hours. The next state will enter the High_notification_state,hide_notification_state,dismiss_state;

Hide_notification_state, this is a temporary state, indicating that the user wants to hide the notification, this state will continue until high_notification_state;

High_notification_state, this state and low_notification_state similar, but does not allow the user to hide the notification, responsible for triggering fired_state or dismiss_state;

Snoozed_state, like High_notification_state, but adds a bit of timed time to complete the delay function;

Fired_state, which indicates a ringing state, will start alarmservice until the user changes it to snoozed_state or dismiss_state, and if the user is left unchecked, it will enter missed_state afterwards;

Missed_state, this state after Fired_state, will give a reminder in the notification bar just before the bell;

Dismiss_state, this status indicates that the timing is over, depending on the setting of the timed item to determine if it needs to be duplicated, to decide whether to delete the item or to continue setting a new timer.

The Setxxxstate method above is the processing of these states, while planning a timed transition to the next state. such as 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);    }

Update alarminstance information while planning for the next state through Scheduleinstancestatechange ():

private static void Scheduleinstancestatechange (context context, Calendar time, alarminstance instance, int new        State) {Long timeinmillis = Time.gettimeinmillis (); LOG.V ("scheduling" + NewState + "to instance" + Instance.mid + "at" + Alarmutils.getforma        Ttedtime (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); }    }

Start a timing through alarmmanager, timing time from the call can see that there is alarminstance, such as in Setsilentstate () The timing is Instance.getlownotificationtime ():

Public Calendar getlownotificationtime () {        Calendar calendar = Getalarmtime ();        Calendar.add (Calendar.hour_of_day, low_notification_hour_offset);        return calendar;    }

The Low_notification_hour_offset value is-2, that is, two hours before the alarm, it will send this low_notification_state broadcast, Alarmstatemanager receives this broadcast processing and then transfers to the next one. The reception of the broadcast in the Onreciever method,

    @Override public void OnReceive (final context context, final Intent Intent) {final Pendingresult result = g        Oasync ();        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 (Ur            i)); 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 (Ur            i)); 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); }    }}

Unified processing in the Handleintent method, distribution of states in Setalarmstate:

public void Setalarmstate (context context, alarminstance instance, int.) {switch (state) {case Ala                RmInstance.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); }    }

For a state that does not transfer the corresponding Setxxxstate method, the next time the transition is completed, a timed loop is formed, until the timer item is deactivated or deleted in the Dismissed_state, and the next timing is obtained if repetition is required.

The overall framework is this way, the use of Alarmmanager in the Alarmstatemanager form a timed state machine, constantly transferred to the next state processing.

The source is here https://android.googlesource.com/platform/packages/apps/DeskClock/+/android-4.4.4_r2.0.1

Timing framework analysis for Android clock applications

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.