您的位置:首页 > 移动开发 > Android开发

Android系统之闹钟模块浅析

2012-11-15 10:36 316 查看
Android系统之闹钟模块浅析
关键词: JNI , HAL,Sqlite
在手机中,闹钟功能是一个最基本的功能,也是我们一个很常用的功能,现在来分析下android是怎么实现的,下面将采用自上而下的方式讲解。

1、APP层
进入桌面闹钟应用程序,选择设置新闹钟,会调到 Alarms.java 里面的

public static long setAlarm(Context context, Alarm alarm) {

....

setNextAlert(context);

....

}

接着也会调用到

public static void setNextAlert(final Context context) {

if (!enableSnoozeAlert(context)) {

//通过查询数据库获取到一个离当前时间最近的alarm

Alarm alarm = calculateNextAlert(context);

if (alarm != null) {

enableAlert(context, alarm, alarm.time);

} else {

disableAlert(context);

}

}

}

然后继续调用到

private static void enableAlert(Context context, final Alarm alarm, final longatTimeInMillis) {

.......

//下面参数RTC_WAKEUP, 就是保证即使系统睡眠了,都能唤醒,闹钟工作

am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);.....

}

/packages/apps/DeskClock/src/com/android/deskclock/Alarms.java

2、Framework层

然后就调用到了AlarmManager.java里面方法

public void set(int type, long triggerAtTime, PendingIntent operation) {

try {

mService.set(type, triggerAtTime, operation);

} catch (RemoteException ex) {

}

}

然后就调用到了AlarmManagerService.java里面方法

public void set(int type, long triggerAtTime, PendingIntent operation) {

setRepeating(type, triggerAtTime, 0, operation);

}

然后继续调用

public void setRepeating(int type, long triggerAtTime, long interval,

PendingIntent operation) {

.....

synchronized (mLock) {

Alarm alarm = new Alarm();

alarm.type = type;

alarm.when = triggerAtTime;

alarm.repeatInterval = interval;

alarm.operation = operation;

// Remove this alarm if already scheduled.

removeLocked(operation);

int index = addAlarmLocked(alarm);

if (index == 0) {

setLocked(alarm);

}

}

}

然后就调用到

private void setLocked(Alarm alarm)

{

......
//mDescriptor 这里的文件是 /dev/alarm

set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);

.....

}

AlarmManagerService程序在系统启动时被系统服务程序SystemServer启动并初始化闹铃设备(/dev/alarm)。

./frameworks/base/core/java/android/app/AlarmManager.java

./frameworks/base/services/java/com/android/server/AlarmManagerService.java

3、JNI的层
JNI : (Java Natative Interface ) 它允许JAVA代码和其他语言的代码进行交互,比如C/C++代码.
private nativevoid set(int fd, int type, long seconds, long nanoseconds);

这就调用到了com_android_server_AlarmManagerService.cpp里面

static JNINativeMethod sMethods[] = {

/* name, signature, funcPtr */

{"init", "()I",(void*)android_server_AlarmManagerService_init},

{"close", "(I)V",(void*)android_server_AlarmManagerService_close},

{"set", "(IIJJ)V",(void*)android_server_AlarmManagerService_set},

{"waitForAlarm", "(I)I",(void*)android_server_AlarmManagerService_waitForAlarm},

{"setKernelTimezone", "(II)I",(void*)android_server_AlarmManagerService_setKernelTimezone},

};

set 对应的是android_server_AlarmManagerService_set,具体是

static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj,jint fd, jint type, jlong seconds, jlong nanoseconds)

{

#if HAVE_ANDROID_OS

struct timespec ts;

ts.tv_sec = seconds;

ts.tv_nsec = nanoseconds;

int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);

if (result < 0)

{

LOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds,nanoseconds, strerror(errno));

}

#endif

}
/frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp

4.HAL层
AndroidHAL层,即硬件抽象层(Hardware abstract layer),是Google响应厂家“希望不公开源码”的要求推出的新概念。

上面的ioctl 就调用到了alarm-dev.c

static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

....

case ANDROID_ALARM_SET(0):

if (copy_from_user(&new_alarm_time, (void __user *)arg,

sizeof(new_alarm_time))) {

rv = -EFAULT;

goto err1;

}

from_old_alarm_set:

spin_lock_irqsave(&alarm_slock, flags);

pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type,

new_alarm_time.tv_sec, new_alarm_time.tv_nsec);

alarm_enabled |= alarm_type_mask;

alarm_start_range(&alarms[alarm_type],

timespec_to_ktime(new_alarm_time),

timespec_to_ktime(new_alarm_time));

spin_unlock_irqrestore(&alarm_slock, flags);

if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)

&& cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)

break;

/* fall though */

....

case ANDROID_ALARM_SET_RTC:

if (copy_from_user(&new_rtc_time, (void __user *)arg,

sizeof(new_rtc_time))) {

rv = -EFAULT;

goto err1;

}

rv = alarm_set_rtc(new_rtc_time);

spin_lock_irqsave(&alarm_slock, flags);

alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;

wake_up(&alarm_wait_queue);

spin_unlock_irqrestore(&alarm_slock, flags);

if (rv < 0)

goto err1;

break;

....

}

然后这边根据用户上层传过来的CMD就调用到了alarm_start_range设置闹钟,alarm_set_rtc 设置RTC时间,这些函数的具体实现在alarm.c文件中;

/* set rtc while preserving elapsed realtime */

int alarm_set_rtc(const struct timespec ts);

下面看alarm.c里面实现:

int alarm_set_rtc(struct timespec new_time)

{

....

ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time);

....

}

上面会调用到了interface.c

intrtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)

{

....

err =rtc->ops->set_time(rtc->dev.parent, tm);

....

}

文件位置

kernel/drivers/rtc/alarm-dev.c

kernel/drivers/rtc/alarm.c

4. Drive层

然后set_time 就看到具体的是那个RTC芯片,这边我们参考rtc-pcf8563.c

static conststruct rtc_class_ops pcf8563_rtc_ops = {

.read_time =pcf8563_rtc_read_time,

.set_time =pcf8563_rtc_set_time,

.read_alarm= pcf8563_rtc_read_alarm,

.set_alarm =pcf8563_rtc_set_alarm,

};

然后就到了

static intpcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)

{

unsignedchar buf[TIME_NUM];

int ret;

ret =data_calc(buf, tm, TIME_NUM);

if (ret < 0)

goto out;

//这边就调用i2c统一接口,往pcf8563rtc芯片寄存器里面写出数据
ret= i2c_smbus_write_i2c_block_data(pcf8563_info->client,PCF8563_RTC_SEC, TIME_NUM, buf);

out:

return ret;

}

文件位置

kernel/drivers/rtc/rtc-pcf8563.c

以上就是闹钟从应用层到驱动层的整个流程。

问题分析
问题一:设置闹钟后,系统如何在后台轮询的
AlarmManagerService 里面有个AlarmThread 会一直轮询/dev/alarm文件,如果打开失败就直接返回,成功就会做一些动作,比如查找时间最近的

alarm,比如睡眠被闹钟唤醒的时候,这边就发一个intent出去,然后在AlarmReceiver.java里面弹出里面会收到就会调用下面的

context.startActivity(alarmAlert);

然后弹出alarm 这个界面

Class c = AlarmAlert.class;

其中publicclass AlarmAlert extends AlarmAlertFullScreen 所以系统睡眠之后被alarm唤醒弹出的alarm就是这边start的

public class AlarmReceiver extends BroadcastReceiver {

/** If the alarm is older than STALE_WINDOW, ignore. It

is probably the result of a time or timezone change */

private final static int STALE_WINDOW = 30 * 60 * 1000;

@Override

public void onReceive(Context context, Intent intent) {

.........

handleIntent(Context context, Intent intent)

........

}

问题二:数据库操作:添加和删除闹钟

当我们设置闹钟的时候,会进入到设置闹钟的窗口,当用户确认点击保存后,会来到 (SetAlarm.java)

private long saveAlarm() {

....

time = Alarms.setAlarm(this, alarm);

......

}

继续跟进会来到(Alarms)

public static long setAlarm(Context context, Alarm alarm){

.....

ContentValues values = createContentValues(alarm);

ContentResolver resolver = context.getContentResolver();

resolver.update(

ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarm.id),

values, null, null);

.....

}

到此就完成了闹钟添加到数据库的功能,想了解其中怎么实现的,可以去了解下

ContentResolver ,ContentProvider,SQLiteOpenHelper .从它们那里能找到你要的答案

问题三: 定时短信

对于这个功能实现的方式很多,我讲下我个人的思路,然后做了个小demo.

思路1:使用AlarmManager的set方法来实现该功能,当时间到时发送短信。

思路2:后台开启一个Server ,采用轮询的方式来实现该功能.

数据存储可以采用sqlite 或者 SharedPerferences 方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: