UML
题目分析
完成手机通讯录备份,要求
- 1.实现可以将通讯录备份到预先设定的服务器数据库中。
- 2.数据库使用SQLite.
- 3.同时备份到TF卡。
- 4.Android下通信部分用C++完成,界面用Java完成,之间采用JNI调用。
- 5.需要有完整的设计文档(UML)
根据需求,我们需要完成两部分:
- Android下获取通讯录信息,传递给C++接口程序上传至服务器,同时保存至SD卡。
- 服务器下接受socket传来的通讯录信息,同时保存至本地数据库。
UML时序图
- 用户打开安卓界面,安卓获取本机通讯录打包好传参给C++接口程序,同时保存数据到SD卡;
- C++接口程序通过TCP/IP连接远程服务器,上传数据;
- 服务器端接收数据,并插入到本地数据库;
UML协作图
效果图
- 上传到服务器并保存到数据库
- 查看数据库
- 通讯录保存到SD卡
- 通讯录写入文件
Android端
界面设计
- 安卓界面放置了一个button,用于上传同时保存通讯录操作;
- textView用于显示上传状态;
- ListView显示本机通讯录;
JNI
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。
新建一个JNITest的Java类
将Android获取的通讯录数据以指针数组的形式传递给C++接口程序。
package com.example.dufaxing.myapplication;
public class JNITest {
static {
System.loadLibrary("JniLib");
}
public native String getString();
public native String upData(String[] arrData);
}
JNI生成头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_dufaxing_myapplication_JNITest */
#ifndef _Included_com_example_dufaxing_myapplication_JNITest
#define _Included_com_example_dufaxing_myapplication_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_dufaxing_myapplication_JNITest
* Method: getString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_dufaxing_myapplication_JNITest_getString
(JNIEnv *, jobject);
/*
* Class: com_example_dufaxing_myapplication_JNITest
* Method: upData
* Signature: ([Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_dufaxing_myapplication_JNITest_upData
(JNIEnv *, jobject, jobjectArray);
#ifdef __cplusplus
}
#endif
#endif
定义C++功能函数
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include<sys/socket.h>
#define MAXLINE 4096
#include <com_example_dufaxing_myapplication_JNITest.h>
/*
* Class: com_example_dufaxing_myapplication_JNITest
* Method: upData
* Signature: ([Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_dufaxing_myapplication_JNITest_upData
(JNIEnv * env, jobject, jobjectArray strArray) {
/*将Java传递的jobjectArray通信录转化为 char* 数组 */
jstring jstr;
jsize len = (env)->GetArrayLength( strArray);
char **pstr = (char **) malloc(len*sizeof(char *));
/*绑定IP地址*/
char servInetAddr[] = "127.0.0.1";
struct sockaddr_in sockaddr;
int socketfd;
char recvline[MAXLINE], sendline[MAXLINE] ;//"dufaxing";
memset(&sockaddr,0,sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
/*绑定端口号*/
sockaddr.sin_port = htons(6666);
for (int i=0 ; i < len; i++) {
jstr = (jstring)(env)->GetObjectArrayElement(strArray, i);
pstr[i] = (char *)(env)->GetStringUTFChars(jstr, 0);
if( (socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return (*env).NewStringUTF("create socket error");
}
if( inet_pton(AF_INET,servInetAddr, &sockaddr.sin_addr) <= 0) {
return (*env).NewStringUTF("inet_pton error");
}
if((connect(socketfd,(struct sockaddr*)&sockaddr,sizeof(sockaddr))) < 0 ) {
return (*env).NewStringUTF("connect error");
}
if((send(socketfd,pstr[i],strlen(pstr[i]),0)) < 0) {
return (*env).NewStringUTF("send mes error");
}
close(socketfd);
sleep(1);
}
return (*env).NewStringUTF("上传成功");
}
保存至SD卡
SD卡权限
Android 6.0之后要获取读取SD卡的权限不仅要在AndroidManifest.xml文件加入:
<!--在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 从SDCard读取数据权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
还需要加入动态申请权限
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"};
public static void verifyStoragePermissions(Activity activity) {
try {
//检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(activity,
"android.permission.WRITE_EXTERNAL_STORAGE");
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
将verifyStoragePermissions(this);
权限申明在onCreate
中。
将通讯录写入TXT文件
// 向SD卡写入数据
private void writeSDcard(String str) {
try {
// 判断是否存在SD卡
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
// 获取SD卡的目录
File sdDire = Environment.getExternalStorageDirectory();
FileOutputStream outFileStream = new FileOutputStream(sdDire.getCanonicalPath() + "/test.txt");
// FileOutputStream outFileStream = new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/test.txt");
outFileStream.write(str.getBytes());
outFileStream.close();
Toast.makeText(this, "数据保存到text.txt文件了", Toast.LENGTH_LONG)
.show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
服务器端
socket通信
以下为socket通信测试代码
/*************************************************************************
> File Name: server.cpp
> Author: dufaxing
> Mail: dufaxing@qq.com
> Created Time: Tue 30 Oct 2018 06:10:26 AM PDT
************************************************************************/
#include<iostream>
using namespace std;
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);//监听的端口号
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
if( listen(listenfd, 10) == -1) {
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
printf("======waiting for client's request======\n");
while(1) {
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) {
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
}
close(listenfd);
return 0;
}
SQLite3
Linux服务器上大多都已经自装了SQLite3,只要使用下面的命令来检查您的机器上是否已经安装了 SQLite。
dufaxing@ubuntu:~$ sqlite3
SQLite version 3.22.0 2018-01-22 18:45:57
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite>
创建数据库
在终端执行命令g++ create_database.cpp -o create_database.out -l sqlite3
可以生成可执行文件,注意需要加入-l sqlite3
编译以加入sqlite3.h
头文件。
/*************************************************************************
> File Name: create_database.cpp
> Author: dufaxing
> Mail: dufaxing@qq.com
> Created Time: Tue 30 Oct 2018 02:38:33 AM PDT
************************************************************************/
#include<iostream>
using namespace std;
#include <stdio.h>
#include <sqlite3.h>
int main(int argc, char* argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("test.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
fprintf(stderr, "Opened database successfully\n");
}
sqlite3_close(db);
}
创建表
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char* argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
/* Open database */
rc = sqlite3_open("test.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
fprintf(stdout, "Opened database successfully\n");
}
/* Create SQL statement */
sql = "CREATE TABLE COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"NAME TEXT NOT NULL );";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
fprintf(stdout, "Table created successfully\n");
}
sqlite3_close(db);
return 0;
}
服务器将通讯录保存到数据库
- 插入数据库时,需严格匹配所创建数据库的数据类型!
/*************************************************************************
> File Name: server.cpp
> Author: dufaxing
> Mail: dufaxing@qq.com
> Created Time: Tue 30 Oct 2018 06:10:26 AM PDT
************************************************************************/
#include<iostream>
using namespace std;
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#define MAXLINE 4096
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
int i;
for(i=0; i<argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char** argv)
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
int id = 1;
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
if( listen(listenfd, 10) == -1) {
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
printf("======waiting for client's request======\n");
while(1) {
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) {
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
/* Open database */
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL statement */
char BUFF[200] = {0};
sprintf (BUFF, "INSERT INTO COMPANY(ID,NAME)" "VALUES (%d, `%s`);", id++, buff);
rc = sqlite3_exec(db, BUFF, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Records created successfully\n");
}
sqlite3_close(db);
}
close(listenfd);
return 0;
}