sqlite 命令与编程

用户设置多个闹钟然后关机,闹钟在之前的实现下会响动一次,这时如果用户选择关机,则后面的闹钟都不会响动了。由此需要在关机闹钟的代码中读取当前用户设置的闹钟,并在用户选择关机时再次设置。众所周知,android 中的用户数据大都储存于 SQLite 数据库中,因此要完成此功能就需要用 c 语言去读取数据库里面信息。在刚开始学习 linux 的时候接触过 SQLite,不过一直都没有仔细用过,这次因此好好总结了一下(感慨下  几个月前自己写的代码自己都快不认识了)。

一、SQLite 常用命令

首先用命令 sqlite3 alarms.db 打开数据库,然后输入 .help,可以看到相关的命令说明及用法介绍:

sqlite> .help
.backup ?DB? FILE      备份数据库到 FILE 指定的文件
.bail ON|OFF           Stop after hitting an error.  Default OFF
.databases             列出当前数据库名称和位置
.dump ?TABLE? ...      打印当前数据库内容
                         If TABLE specified, only dump tables matching
                         LIKE pattern TABLE.
.echo ON|OFF           Turn command echo on or off
.exit                  退出
.explain ?ON|OFF?      Turn output mode suitable for EXPLAIN on or off.
                         With no args, it turns EXPLAIN on.
.header(s) ON|OFF      打开或者关闭表头显示
.help                  Show this message
.import FILE TABLE     Import data from FILE into TABLE
.indices ?TABLE?       列出所有的目录名称
                         If TABLE specified, only show indices for tables
                         matching LIKE pattern TABLE.
.load FILE ?ENTRY?     Load an extension library
.log FILE|off          Turn logging on or off.  FILE can be stderr/stdout
.mode MODE ?TABLE?     设置显示模式
                         csv      Comma-separated values
                         column   Left-aligned columns.  (See .width)
                         html     HTML <table> code
                         insert   SQL insert statements for TABLE
                         line     One value per line
                         list     Values delimited by .separator string
                         tabs     Tab-separated values
                         tcl      TCL list elements
.nullvalue STRING      这是 null 字符串显示为 STRING
.output FILENAME       将输出定向到 FILENAME 文件中
.output stdout         Send output to the screen
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  退出
.read FILENAME         导入文件数据
.restore ?DB? FILE     Restore content of DB (default "main") from FILE
.schema ?TABLE?        查看表的创建语句
                         If TABLE specified, only show tables matching
                         LIKE pattern TABLE.
.separator STRING      设置新的分隔符
.show                  显示当前配置信息
.stats ON|OFF          Turn stats on or off
.tables ?TABLE?        列出表的名称
                         If TABLE specified, only list tables matching
                         LIKE pattern TABLE.
.timeout MS            Try opening locked tables for MS milliseconds
.width NUM1 NUM2 ...   Set column widths for "column" mode
.timer ON|OFF          Turn the CPU timer measurement on or offor off

更多详细用法参考Blog:SQLite 入门教程,当然 SQLite 支持 SQL 语句,通过如下语句查询 alarm.db 中的数据获取用户的设置情况:

sqlite> select * from alarms;
1|6|10|31|0|1|1||content://settings/system/alarm_alert
2|9|0|96|0|0|1||
更详细的信息可以通过 .dump 命令获取:

sqlite> .dump alarms
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE alarms (_id INTEGER PRIMARY KEY,hour INTEGER, minutes INTEGER, daysofweek INTEGER, alarmtime INTEGER, 
enabled INTEGER, vibrate INTEGER, message TEXT, alert TEXT);
INSERT INTO "alarms" VALUES(1,6,10,31,0,1,1,'','content://settings/system/alarm_alert');
INSERT INTO "alarms" VALUES(2,9,0,96,0,0,1,'','');
COMMIT;
这里可以看到每个字段代表的含义:id、小时、分钟、星期、响闹时间、使能、振动、消息、铃音。

二、SQLite 编程简介

在 c 代码中想要查询数据库里面的内容,可以通过以下几个接口对数据库进行操作:

1、sqlite3_open

在操作数据库之前需要先通过该接口打开,这个函数的原型为:

SQLITE_API int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);
函数会打开 filename 指定的数据库,并通过 ppDb 返回数据库的连接对象,如果指定的数据库不存在则创建一个同名的数据库,数据库采用 UTF-8 的编码方式。
2、sqlite3_prepare

这个函数将一条 SQL 语句转换为一个准备语句对象,同时返回这个对象的指针(Statement handle),函数原型如下:

SQLITE_API int sqlite3_prepare(
  sqlite3 *db,            /* Database handle */
  const char *zSql,       /* SQL statement, UTF-8 encoded */
  int nByte,              /* Maximum length of zSql in bytes. */
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
);
其中 db 为 sqlite3_open 的返回值,zSql 为 SQL 语句,nByte 一般设置为 -1,ppStmt 为准备语句指针。

3、sqlite3_step
该函数执行 sqlite3_prepare 创建好的准备语句,原型如下:

SQLITE_API int sqlite3_step(sqlite3_stmt*);
执行的结果集保存在参数中以便后续使用。

4、sqlite3_column_int

这个函数从 sqlite3_step 执行完后的结果集中返回指定列的值,函数原型为:

SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol);
其中 iCol 指定了想要返回的列的索引值,这个函数的返回值为 int 类型。

5、sqlite3_finalize

这个函数用于销毁之前创建的准备语句,以防内存泄露,原型如下:

SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
6、sqlite3_close

函数用于关闭打开的数据库。在关机闹钟里面操作数据库的代码如下:

static int read_alarm_from_db(void)
{
	int id, ret = 0;
	sqlite3* db = 0;
	sqlite3_stmt* stmt = 0;

	ret = sqlite3_open("/data/data/com.android.deskclock/databases/alarms.db", &db);
	if (ret != SQLITE_OK) {
		sqlite3_close(db);
		LOGE("Could not open alarms.db\n");
		return ret;
	}

	ret = sqlite3_prepare(db, "select * from alarms", -1, &stmt, 0);
	if (ret != SQLITE_OK) {
		sqlite3_close(db);
		LOGE("Could not execute SELECT\n");
		return ret;
	}

	while (sqlite3_step(stmt) == SQLITE_ROW) {
		alarm_num = sqlite3_column_int(stmt, 0);
	}

	alarm_time = malloc(alarm_num * sizeof(struct alarm_time_from_db));
	if (alarm_time == NULL) {
		sqlite3_close(db);
		LOGE("malloc fail.\n");
		return -1;
	}

	while (sqlite3_step(stmt) == SQLITE_ROW) {
		id = sqlite3_column_int(stmt, 0);
		alarm_time[id-1].hour    = sqlite3_column_int(stmt, 1);
		alarm_time[id-1].min     = sqlite3_column_int(stmt, 2);
		alarm_time[id-1].week    = sqlite3_column_int(stmt, 3);
		alarm_time[id-1].enable  = sqlite3_column_int(stmt, 5);

		LOGI("alarms: id = %d, time = %02d:%02d, week = %d, enable = %d\n", id - 1,
			alarm_time[id-1].hour, alarm_time[id-1].min, alarm_time[id-1].week, alarm_time[id-1].enable);
	}

	sqlite3_finalize(stmt);
	sqlite3_close(db);

	return SQLITE_OK;
}

根据数据库中获取的用户设置,设计了一个巧妙的算法用于计算下一次闹钟的时间。在数据库的记录中,记录的是某些天的响闹时间以及星期数(每次循环为一个星期),因此以星期天(day0)开始计算当前经历的分钟数以及闹钟设置时间的分钟数,并从今天开始遍历闹钟时间,以时间最近的一次闹钟时间作为返回值,算法如下:

static long find_next_alarm(void)
{
	time_t now_time;
	struct tm real_time;
	long next_alarm = -1;
	unsigned int week_mask = 0;
	int i, id, weekday, now_min, alarm_min, sub_min, first_id = -1;

	time(&now_time);
	get_real_time(&real_time);
	now_min = real_time.tm_wday * 1440 + real_time.tm_hour * 60 + real_time.tm_min;  /* The minutes since Sunday */

	weekday = real_time.tm_wday;
	for (i = 0; i < DAY_OF_WEEK; i++, weekday++) {
		sub_min = 1440 * (i + 1);       /* alarm_min - now_min */

		/*
		 *            Sunday   Monday   ...
		 *  tm_wday     0        1      ...
		 * week_mask   2^6      2^0     ...
		 *
		 */
		if (weekday > 0)
			week_mask = 1 << ((weekday - 1) % DAY_OF_WEEK);
		else
			week_mask = 1 << 6;

		/* find the next alarm */
		for (id = 0; id < alarm_num; id++) {
			if (alarm_time[id].enable && ((alarm_time[id].week & week_mask) || alarm_time[id].week == 0)) {
				alarm_min = weekday * 1440 + alarm_time[id].hour * 60 + alarm_time[id].min;
				if ((alarm_min - now_min > 0) && (alarm_min - now_min < sub_min)) {
					first_id = id;
					sub_min  = alarm_min - now_min;
				}
			}
		}

		/* calculation the next alarm by secnods */
		if (first_id >= 0) {
			next_alarm = now_time + sub_min * 60;
			LOGI("find next alarm: id = %d, now_time = %ld, sub_min = %d\n", first_id, now_time, sub_min);
			break;
		}
	}

	return next_alarm;
}

附: Android.mk

在调用 sqlite 的相关接口时需要配置头文件路径以及库文件如下:

LOCAL_C_INCLUDES := external/sqlite
LOCAL_SHARED_LIBRARIES := libsqlite

还需要将强制静态执行关掉:

#LOCAL_FORCE_STATIC_EXECUTABLE := true
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值