dufaxing To be a better man

Android调用CPP接口实现通讯录上传与备份

2018-11-15


UML

题目分析

完成手机通讯录备份,要求
- 1.实现可以将通讯录备份到预先设定的服务器数据库中。
- 2.数据库使用SQLite.
- 3.同时备份到TF卡。
- 4.Android下通信部分用C++完成,界面用Java完成,之间采用JNI调用。
- 5.需要有完整的设计文档(UML)

根据需求,我们需要完成两部分:

  • Android下获取通讯录信息,传递给C++接口程序上传至服务器,同时保存至SD卡。
  • 服务器下接受socket传来的通讯录信息,同时保存至本地数据库。

UML时序图

  • 用户打开安卓界面,安卓获取本机通讯录打包好传参给C++接口程序,同时保存数据到SD卡;
  • C++接口程序通过TCP/IP连接远程服务器,上传数据;
  • 服务器端接收数据,并插入到本地数据库;

F9CmFA.png

UML协作图

F9A0Z8.png

效果图

  • 上传到服务器并保存到数据库

ixVfHg.png

  • 查看数据库

ixV5Nj.png

  • 通讯录保存到SD卡

Fk3JJJ.png

  • 通讯录写入文件

Fk3YW9.png


Android端

界面设计

  • 安卓界面放置了一个button,用于上传同时保存通讯录操作;
  • textView用于显示上传状态;
  • ListView显示本机通讯录;

F9kbb8.md.png

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;
}


Similar Posts

Comments