sqliteコマンドとプログラミング

8656 ワード

ユーザは複数の目覚まし時計を設定してシャットダウンし,目覚まし時計は前の実装で1回鳴動するが,ユーザがシャットダウンを選択すると,後の目覚まし時計は鳴動しない.これにより、シャットダウンアラームのコードに現在のユーザが設定しているアラームを読み出し、ユーザがシャットダウンを選択したときに再度設定する必要がある.周知のように、androidのユーザーデータの多くはSQLiteデータベースに格納されているため、この機能を完了するにはc言語でデータベース内の情報を読み取る必要があります.linuxの勉強を始めたばかりの頃にSQLiteに触れたことがありますが、ずっと丁寧に使っていなかったので、今回はしっかりまとめてみました(数ヶ月前に自分が書いたコードを自分でも知らないような気がします).
一、SQLite常用コマンド
まずコマンドで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がsqlite 3_Openの戻り値は、zSqlがSQL文、nByteが-1、ppStmtが準備文ポインタに設定されます.
3、sqlite3_Stepこの関数はsqlite 3_を実行するprepareは、次のような準備文を作成します.
SQLITE_API int sqlite3_step(sqlite3_stmt*);
で実行された結果セットは、後で使用するためにパラメータに保存されます.
4、sqlite3_column_int
この関数はsqlite 3_から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
"); return ret; } ret = sqlite3_prepare(db, "select * from alarms", -1, &stmt, 0); if (ret != SQLITE_OK) { sqlite3_close(db); LOGE("Could not execute SELECT
"); 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.
"); 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
", 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; }

データベースで取得したユーザ設定に基づいて,次の目覚まし時計の時間を計算するための巧みなアルゴリズムを設計した.データベースの記録では、ある日の目覚まし時間と曜日(サイクルごとに1週間)が記録されているため、現在経過している分数と目覚まし時計の設定時間の分数の計算を日曜日(day 0)から開始し、今日から目覚まし時計の時間を巡り、最も近い目覚まし時計の時間を戻り値としてアルゴリズムは次のようになります.
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
", 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