数据库大家都知道,他是一组经过计算机整理后的数据,存储在一个或多个文件中,便于快速查询,分析的一种先进工具,同样,在Android中也有数据库,现有的应用有存贮联系人,MP3歌曲,将来还可作为如:网络服务器数据库,日志记录数据库,多媒体信息收集数据库,地图信息数据库等等,为此则编写一个应用 SQLite 数据库的简单示例。
一、在 ndk 上 sqlite 的示例:
在 android 中 已有 SQLite 库,使用方法与官方网站上的接口一致。如下则是一个使用SQLite接口写的C++示例代码:
1.建立gmain_mysqlite.cpp
/* -- START -- */
#include <sqlite3.h>
#include <stdio.h>
static int _sql_callback(void * notused, int argc, char ** argv, char ** szColName)
{
int i;
for ( i=0; i < argc; i++ )
{
printf( "%s = %s\n", szColName, argv == 0 ? "NUL" : argv );
}
return 0;
}
int main()
{
const char * sSQL1 = "create table users(userid varchar(20) PRIMARY KEY,age int,regtime datetime);";
const char * sSQL2 = "insert into users values('guilh',29,'2009-7-16');";
const char * sSQL3 = "select * from users;";
sqlite3 * db = 0;
char * pErrMsg = 0;
int ret = 0;
const char* const dbname = "gtest.db";
//打开数据库,如果数据库不存在,会建立一个数据库
ret = sqlite3_open(dbname, &db);
if ( ret != SQLITE_OK )
{
printf("open error! : %s", sqlite3_errmsg(db));
return(1);
}
printf("open db OK!\n");
// 执行建表SQL
sqlite3_exec( db, sSQL1, 0, 0, &pErrMsg );
if ( ret != SQLITE_OK )
{
//fprintf(stderr, "SQL error: %s\n", pErrMsg);
printf("SQL error: %s\n", pErrMsg);
sqlite3_free(pErrMsg);
}
// 执行插入记录SQL
sqlite3_exec( db, sSQL2, 0, 0, &pErrMsg);
// 查询数据表
sqlite3_exec( db, sSQL3, _sql_callback, 0, &pErrMsg);
// 关闭数据库
sqlite3_close(db);
db = 0;
return 0;
}
/* -- END -- */
操作有五步:打开/创建数据库,创建数据表,插入记录,查询数据表并显示,关闭数据库。
这里 _sql_callback 函数是个回调函数 可以从中取得查询内容。
2.建立 gmain_mysqlite.cpp 的 Android.mk
(此文件存于 sources/mysqlite/testmysqlite/Android.mk)
#### START ####
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../config.mk
LOCAL_SRC_FILES:= \
gmain_mysqlite.cpp
LOCAL_CFLAGS := \
-I$(ANDROID_FRAMEWORKS_BASE_INCLUDE) \
-I$(ANDROID_SYSTEM_CORE_INCLUDE) \
-I$(ANDROID_EXTERNAL_SQLITE_INCLUDE) \
-I$(LOCAL_PROJECT_PATH)
LOCAL_LDLIBS := \
-L$(ANDROID_LIB) \
-llog \
-lsqlite \
-licuuc \
-licui18n \
-licudata \
-licuuc
#$(info LOCAL_CFLAGS=$(LOCAL_CFLAGS))
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := testmysqlite.bin
include $(BUILD_EXECUTABLE)
### END ###
说明:
这些使用到Android 下的 SQLite库,需要指定些环境变量。
以上的各变量说明可参阅: 在NDK上建立第一个自己的项目 在此不在做说明。
其中,include $(LOCAL_PATH)/../config.mk # 这里的 config 使用的是 sources/mysqlite/config.mk 此文件存有项目环境变量。来看一下这两个config.mk的变量设定。
sources/mysqlite/config.mk
#### START ####
include usr/config/config.mk
LOCAL_PROJECT_PATH := $(ROOT_SRC)/mysqlite
### END ###
usr/config/config.mk
### START ###
ROOT_PATH := $(PWD)
ROOT_SRC := $(ROOT_PATH)/sources
#此变量根据不同环境配置
HOME_PATH :=
ANDROID_SRC := $(HOME_PATH)/android
ANDROID_FRAMEWORKS_BASE_INCLUDE := $(ANDROID_SRC)/frameworks/base/include
ANDROID_SYSTEM_CORE_INCLUDE := $(ANDROID_SRC)/system/core/include
ANDROID_DALVIK_INCLUDE := $(ANDROID_SRC)/dalvik/libnativehelper/include
ANDROID_EXTERNAL := $(ANDROID_SRC)/external
ANDROID_EXTERNAL_SQLITE_INCLUDE := $(ANDROID_EXTERNAL)/sqlite/dist
ANDROID_LIB := $(ANDROID_SRC)/out/target/product/generic/system/lib
### END ###
详细说明可参阅 借用 android 源码中的 logcat。
至此重要部份就已完成,如果需要编译尝试,还需要增加相关项目目录及配置。
二、 项目配置及目录结构:
需要建立三个文件:default.propertie、Application.mk、sources/mysqlite/Android.mk
apps/mysqlite/default.propertie
#### START ####
target=android-3
### END ###
apps/mysqlite/Application.mk
#### START ####
APP_PROJECT_PATH := $(call my-dir)
APP_MODULES := testmysqlite.bin
### END ###
sources/mysqlite/Android.mk
#### START ####
#可寻找到当前目录下的所有子目录中的Android.mk
include $(call all-subdir-makefiles)
### END ###
至此可以看到整体结构如下:
# java项目目录、编译启动文件存放位置、
# 最终编译结果将从out目录中拷贝一份到apps目录下的 libs 目录中
apps
# C++项目目录
sources
# 用户自定义全局目录,如 config、include、lib等
usr
# 用户自定义全局配置makefile文件。
usr/config/config.mk
# mysqlite 项目下总makefile 和一个配置文件
apps/mysqlite/default.propertie
apps/mysqlite/Application.mk
# mysqlite 项目下的文件
sources/mysqlite/Android.mk
sources/mysqlite/config.mk
sources/mysqlite/testmysqlite/Android.mk
sources/mysqlite/testmysqlite/gmain_mysqlite.cpp
接下来在~/android-ndk-1.5_r1下进行编译并安装到sdk中测试,此时会遇到一个问题。
因为 在SQLite打开/创建数据库操作时,需要 android 系统有可读写文件权限。
如果没有设置权限,运行后会提示错误为 : unable to open database file
解决办法可用如下命令
~/android-sdk-linux_x86-1.5_r2/tools $adb shell
# mount -o remount rw /
然后在运行
# /system/bin/testmysqlite.bin
======================================================
在Android 中会运用到各种对外传输,网络传输方式是其中之一,在未来的开发中可能会应用于网络传输,比如做为服务器使用,或是终端媒体设备等等,如下则是使用NDK编译运行于Android 中的一个简单的网络C/S程序示例。
一、在 ndk 上 socket 的示例:
从 android 的架构中可以得知,android是基于Linux 内核的一个操作系统,所以在Linux上的很多系统接口与在android上的系统接口基本是一致。则可用系统接口操作socket来完成 client /server 的网络通讯功能。
1.建立服务端
其操作过程:创建本地socket,绑定监听本地socket,然后进入循环,并挂起一直等待客户端的请求,当有请求来时,通过判断获得客户端 socket,然后建立子进程,在子进程里向客户端发送消息。
服务端的代码如下: gmain_mynetserver.cpp
/* -- START -- */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVPORT 3333 // 服务器监听端口号
#define BACKLOG 10 //最大同时连接请求数
int main()
{
int sockfd,client_fd; // sockfd:监听socket;client_fd:数据传输socket
struct sockaddr_in my_addr; // 本机地址信息
struct sockaddr_in remote_addr; // 客户端地址信息
// 创建监听 socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("socket create error!\n");
return 1;
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(SERVPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
//绑定 socket
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
{
printf("bind error!\n");
close(sockfd);
return 1;
}
// 监听 socket
if (listen(sockfd, BACKLOG) == -1)
{
printf("listen error!\n");
close(sockfd);
return 1;
}
while ( true )
{
int sin_size = sizeof(struct sockaddr_in);
// 获得客户端请求 socket
if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1)
{
printf("accept error!\n");
continue;
}
printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
if (!fork()) // 子进程代码段
{
// 向客户端发送消息
if ( send(client_fd, "Hello, you are connected!\n", 26, 0) == -1)
{
printf("send error!");
close(client_fd);
close(sockfd);
return 1;
}
// 关闭客户端socket
close(client_fd);
}
}
// 本地socket
close(sockfd);
return 1;
}
/* -- END -- */
2.建立客户端
其操作过程:创建本地socket,与服务端建立链接,然后接收服务端发来的消息。
客户端的代码如下: gmain_mynetclient.cpp
/* -- START -- */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SERVPORT 3333 // 服务端端口
#define MAXDATASIZE 100 // 每次最大数据传输量
int main(int argc, char *argv[])
{
int sockfd, recvbytes;
char buf;
struct hostent *host;
struct sockaddr_in serv_addr;
if (argc < 2)
{
fprintf(stderr,"Please enter the server's hostname!\n");
return 1;
}
//获得输入的服务端IP地址
if(( host = gethostbyname(argv[1])) == NULL)
{
herror("gethostbyname error!");
return 1;
}
//创建本地socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket create error!");
return 1;
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
//与服务端建立链接
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
{
perror("connect error!");
return 1;
}
// 接收数据
if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1)
{
perror("recv error !");
return 1;
}
buf = '\0';
printf("Received: %s",buf);
// 关闭本地socket
close(sockfd);
return 0;
}
二、运行输出:
先执行服务端执行程序 service.bin ,服务端会等待客户端连接
然后在执行客户端执行程序 client.bin ,输入 client.bin <server IP>
服务端接收到客户端请求后会显示"received a connection from <client IP>"
此时服务端会向客户端发送消息。
客户端则会显示服务端发来的消息 "Hello, you are connected!"