AndroidStudio下NDK开发流程

使用Android Studio进行NDK开发时,可在创建Android Studio项目时选择创建C++项目,创建好之后,默认会在src/main/下创建一个cpp的文件夹,C/C++相关的文件就存放在这个文件夹中;在app下面的build.gradle中有NDK的相关配置

一、NDK开发进行文件加密解密

1、实现Java层native方法

  • 创建Android项目
  • 写一个含有加密和解密按钮的view

    `

- 编写一个实现对文件加密解密的java类: Crpytor.java

package com.example.ndk.filecrypt;

public class Cryptor {

static {
    System.loadLibrary("cryptor");
}

/**
 * 对文件进行加密
 * @param path 需要加密的文件路径
 * @return 加密后的文件路径
 */
public native static void crypt(String path,String cryptPath);

/**
 * 对文件进行解密
 * @param cryptPath 加密文件的路径
 * @return 解密后的文件路径
 */
public native static void decrypt(String cryptPath,String decryptPath);

}

- 在View对应的java类中实现相应的点击事件

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

/**
 * 加密的点击事件
 *
 * @param view
 */
public void mCrypt(View view) {
    File sdDir = Environment.getExternalStorageDirectory();
    String sdpath = sdDir.getAbsolutePath();

    String path = sdpath + "/ndk.jpg";
    String cryptPath = sdpath + "/ndk_crypt.jpg";
    Cryptor.crypt(path, cryptPath);
    Toast.makeText(this, "加密完成", Toast.LENGTH_SHORT).show();
}

/**
 * 解密的点击事件
 *
 * @param view
 */
public void mDecrypt(View view) {
    File sdDir = Environment.getExternalStorageDirectory();
    String sdpath = sdDir.getAbsolutePath();

    String cryptPath = sdpath + "/ndk_crypt.jpg";
    String decryptPath = sdpath + "/ndk_decrypt.jpg";
    Cryptor.decrypt(cryptPath, decryptPath);
    Toast.makeText(this, "解密完成", Toast.LENGTH_SHORT).show();
}

}

### 2、使用 javah 命令生成头文件
- 执行javah命令,因为AndroidStudio使用的是UTF-8的编码,所以在执行javah命令时需要指定编码为UTF-8(默认为GBK);

javah -encoding UTF-8 com.example.ndk.filecrypt.Cryptor

- 生成 com_example_ndk_filecrypt_Cryptor.h 文件

### 3、创建JNI/CPP目录,添加NDK本地支持
- 在src/main目录下创建jni/cpp目录,将刚刚生成的.h文件复制到该目录下
- 在项目配置中设置 Android NDK location 目录,需要提前下载 NDK 相关支持 (在Android SDK 下载中选择 SDK Tools 中的 LLDB 、CMake 、NDK 三项进行下载)
- 在jni 目录下创建 CMakeLists.txt 文件

cmake 版本

cmake_minimum_required(VERSION 3.4.1)

添加支持

add_library( # 为library设置名称

cryptor

# 设置该library为共享的
         SHARED

         # 提供C/C++相关文件的相对路径
         cryptor.c )

find_library( # Sets the name of the path variable.

log-lib

# Specifies the name of the NDK library that
          # you want CMake to locate.
          log )

target_link_libraries( # Specifies the target library.与上面add_library的名称相同

cryptor

# Links the target library to the log library
                   # included in the NDK.
                   ${log-lib} )
- 在 app目录下的build.gradle 文件中配置jni 目录,以及NDK支持

android {

defaultConfig {
    // NDK的配置
    ndk{
         moduleName "cryptor"
        abiFilters  "armeabi-v7a", "x86"
    }
}

sourceSets {
    //配置jni目录
    main {
        jni.srcDirs = []
    }
}

externalNativeBuild {
    //配置cmake文件路径
    cmake {
        path file('src/main/jni/CMakeLists.txt')
    }
}

}

### 4、实现头文件中定义的函数

#include “com_example_ndk_filecrypt_Cryptor.h”

#include <string.h>

char password[] = “qazwsxedc”;

//加密

char

crypt(char normal_path[], char crypt_path[]) {

//打开文件


normal_fp = fopen(normal_path, “rb”);

FILE *crypt_fp = fopen(crypt_path, “wb”);

//一次读取一个字符

int ch;

int i = 0;

int pwd_len = strlen(password);

while ((ch = fgetc(normal_fp)) != EOF) {//End of FILE

//加密

fputc(ch ^ password[i % pwd_len], crypt_fp);

i++;

}

fclose(normal_fp);

fclose(crypt_fp);

}

//解密

char

decrypt(char crypt_path[], char decrypt_path[]) {

//打开文件


crypt_fp = fopen(crypt_path, “rb”);

FILE *decrypt_fp = fopen(decrypt_path, “wb”);

//一次读取一个字符

int ch;

int i = 0;

int pwd_len = strlen(password);

while ((ch = fgetc(crypt_fp)) != EOF) {//End of FILE

//加密

fputc(ch ^ password[i % pwd_len], decrypt_fp);

i++;

}

fclose(crypt_fp);

fclose(decrypt_fp);

}

/**

  • 加密
  • @param env
  • @param cls
  • @param jstr
  • @return

    */

    JNIEXPORT void JNICALL Java_com_example_ndk_filecrypt_Cryptor_crypt
    (JNIEnv *env, jclass cls, jstring normal_path_str, jstring crypt_path_str) {

    char path = (
    env)->GetStringUTFChars(env, normal_path_str, NULL);

    char crypt_path = (
    env)->GetStringUTFChars(env, crypt_path_str, NULL);

    crypt(path, crypt_path);

    //对变量的内存进行释放

    (

    env)->ReleaseStringChars(env, normal_path_str, path);

    (


    env)->ReleaseStringChars(env, crypt_path_str, crypt_path);

    }

/**

  • 解密
  • @param env
  • @param cls
  • @param jstr
  • @return

    */

    JNIEXPORT void JNICALL Java_com_example_ndk_filecrypt_Cryptor_decrypt
    (JNIEnv *env, jclass cls, jstring crypt_path_str, jstring decrypt_path_str) {

    char crypt_path = (
    env)->GetStringUTFChars(env, crypt_path_str, NULL);

    char decrypt_path = (
    env)->GetStringUTFChars(env, decrypt_path_str, NULL);

    decrypt(crypt_path, decrypt_path);

    //对变量的内存进行释放

    (

    env)->ReleaseStringChars(env, crypt_path_str, crypt_path);

    (


    env)->ReleaseStringChars(env, decrypt_path_str, decrypt_path);

    }

### 5、编译生成.so动态库
- 点击Build -> Make Module app 
- 可以在app -> build -> intermediates -> cmake -> debug -> obj 下看到对应的armeabi-v7a 和 x86 的so库

### 6、加载.so动态库,运行程序
- 在src/main 目录下创建 jniLibs 文件夹
- 将上面生成的 armeabi-v7a 和 x86 的so库 复制到该 jniLibs 文件夹内
- 运行程序


## 二、NDK开发进行文件拆分与合并
### 1、实现Java层native方法
- 创建Android项目
- 写一个含有加密和解密按钮的view
<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="拆分"
    android:onClick="mDiff"/>

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="合并"
    android:onClick="mPatch"/>

- 编写一个实现对文件拆分合并的java类: FilePatchUtils.java

package com.example.ndk.filepatch

public class FilePatchUtils {

static {

System.loadLibrary(“filepatch-lib”);

}

/**
 * 对文件进行拆分
 * @param path  文件路径
 * @param count 拆分成多少个
 */
public native static void diff(String path,String pathPattern,int count);

/**
 * 对文件进行合并
 * @param pathPattern 需合并文件路径(%d)
 * @param count 将多少个文件合并
 * @param patchPath 合并后文件路径
 */
public native static void patch(String pathPattern,int count,String patchPath);

}

- 在View对应的java类中实现相应的点击事件

public class FilePatchActivity extends AppCompatActivity {

private String sdpath;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_file_patch);
    File sdDir = Environment.getExternalStorageDirectory();
    sdpath = sdDir.getAbsolutePath();
}

 /**
 * 文件拆分
 *
 * @param view
 */
public void mDiff(View view) {
    String path = sdpath + File.separatorChar + "ndk.jpg";
    String pathPattern = sdpath + File.separatorChar + "ndk_%d.jpg";
    FilePatchUtils.diff(path, pathPattern, 3);
    Toast.makeText(this, "拆分完成", Toast.LENGTH_SHORT).show();
}

/**
 * 文件合并
 *
 * @param view
 */
public void mPatch(View view) {
    String patchPath = sdpath + File.separatorChar + "ndk_patch.jpg";
    String pathPattern = sdpath + File.separatorChar + "ndk_%d.jpg";
    FilePatchUtils.patch(pathPattern, 3, patchPath);
    Toast.makeText(this, "合并完成", Toast.LENGTH_SHORT).show();
}

}

### 2、使用 javah 命令生成头文件
- 执行javah命令,因为AndroidStudio使用的是UTF-8的编码,所以在执行javah命令时需要指定编码为UTF-8(默认为GBK);

javah -encoding UTF-8 com.example.ndk.filepatch.FilePatchUtils

- 生成 com_example_ndk_filepatch_FilePatchUtils.h 文件

### 3、创建JNI目录,添加NDK本地支持
- 在src/main目录下创建jni目录,将刚刚生成的.h文件复制到该目录下
- 在项目配置中设置 Android NDK location 目录,需要提前下载 NDK 相关支持 (在Android SDK 下载中选择 SDK Tools 中的 LLDB 、CMake 、NDK 三项进行下载)
- 在jni 目录下创建 CMakeLists.txt 文件;同上;
- 在 app目录下的build.gradle 文件中配置jni 目录,以及NDK支持;同上;

### 4、实现头文件中定义的函数

#include “com_example_ndk_filepatch_FilePatchUtils.h”

#include <android/log.h>

#define LOGI(FORMAT, …) android_log_print(ANDROID_LOG_INFO,”FILE_PATCH”,FORMAT,
VA_ARGS__);

#define LOGE(FORMAT, …) android_log_print(ANDROID_LOG_ERROR,”FILE_PATCH”,##FORMAT,
VA_ARGS__);

/**

  • 获取文件大小
  • @param path 文件路径
  • @return 文件大小

    /

    long get_file_size(char


    path) {

    FILE *fp = fopen(path, “r”);

    if (fp == NULL) {

    return 0;

    }

    fseek(fp, 0, SEEK_END);

    return ftell(fp);

    }

/*

  • Class: com_example_ndk_filepatch_FilePatchUtils
  • Method: diff
  • Signature: (Ljava/lang/String;Ljava/lang/String;I)V

    */

    JNIEXPORT void JNICALL Java_com_example_ndk_filepatch_FilePatchUtils_diff

    (JNIEnv *env, jclass cls, jstring file_path_str, jstring pattern_str, jint file_count) {

    const char path = (
    env)->GetStringUTFChars(env, file_path_str, NULL);

    const char pattern = (
    env)->GetStringUTFChars(env, pattern_str, NULL);

    //得到分割之后的文件的路径列表

    char * filePaths = malloc(sizeof(char
    ) * file_count);

    //读取path对应路径,循环写入子文件

    int i = 0;

    for (; i < file_count; i++) {

    //给元素开辟空间
     filePaths[i] = malloc(sizeof(char) * 100);
     //给元素赋值
     sprintf(filePaths[i], pattern, (i + 1));
     LOGI("patch path:%s", filePaths[i]);

    }

    //分割文件

    FILE *fp = fopen(path, “rb”);

    int fileSize = get_file_size(path);

    if (fileSize % file_count == 0) {

    //能整除
     int part = fileSize / file_count;
     //逐一写入分割子文件中
     int i = 0;
     for (; i < file_count; i++) {
         FILE *fwp = fopen(filePaths[i], "wb");
         int j = 0;
         for (; j < part; j++) {
             fputc(fgetc(fp), fwp);
         }
         fclose(fwp);
     }
     fclose(fp);

    } else {

    //不能整除
     int part = fileSize / (file_count - 1);
     //逐一写入分割子文件中
     int i = 0;
     for (; i < file_count - 1; i++) {
         FILE *fwp = fopen(filePaths[i], "wb");
         int j = 0;
         for (; j < part; j++) {
             fputc(fgetc(fp), fwp);
         }
         fclose(fwp);
     }
     part = fileSize % (file_count - 1);
     if (part > 0) {
         FILE *fwp = fopen(filePaths[file_count - 1], "wb");
         int j = 0;
         for (; j < part; j++) {
             fputc(fgetc(fp), fwp);
         }
         fclose(fwp);
     }
     fclose(fp);

    }

    //释放

    i = 0;

    for (; i < file_count; i++) {

    free(filePaths[i]);

    }

    free(filePaths);

    //对变量的内存进行释放

    (

    env)->ReleaseStringChars(env, file_path_str, path);


    env)->ReleaseStringChars(env, pattern_str, pattern);

    }

/*

  • Class: com_example_ndk_filepatch_FilePatchUtils
  • Method: patch
  • Signature: (Ljava/lang/String;Ljava/lang/String;I)V

    */

    JNIEXPORT void JNICALL Java_com_example_ndk_filepatch_FilePatchUtils_patch
    (JNIEnv *env, jclass cls, jstring pattern_str, jint count, jstring patch_path_str) {

    char pattern = (
    env)->GetStringUTFChars(env, pattern_str, NULL);

    char patch_path = (
    env)->GetStringUTFChars(env, patch_path_str, NULL);

    FILE *fwp = fopen(patch_path, “wb”);

    int i = 0;

    for (; i < count; i++) {

    //单个文件逐一写入fwp
     char *path = malloc(sizeof(char) * 100);
     sprintf(path, pattern, (i + 1));
     LOGI("patch path:%s", path);
     FILE *frp = fopen(path, "rb");
     //判断文件是否为NULL
     if (frp != NULL) {
         //获取单个文件大小
         int file_size = get_file_size(path);
         int j = 0;
         for (; j < file_size; ++j) {
             fputc(fgetc(frp), fwp);
         }
         fclose(frp);
     }
     //释放分配的内存空间
     free(path);

    }

    fclose(fwp);

    //对变量的内存进行释放

    (

    env)->ReleaseStringChars(env, pattern_str, pattern);

    (


    env)->ReleaseStringChars(env, patch_path_str, patch_path);

    }

`

5、编译生成.so动态库

  • 点击Build -> Make Module app
  • 可以在app -> build -> intermediates -> cmake -> debug -> obj 下看到对应的armeabi-v7a 和 x86 的so库

6、加载.so动态库,运行程序

  • 在src/main 目录下创建 jniLibs 文件夹
  • 将上面生成的 armeabi-v7a 和 x86 的so库 复制到该 jniLibs 文件夹内
  • 运行程序

原文 

https://blog.onestravel.cn/20190301/android-ndk/androidstudio-xia-jin-xing-ndk-kai-fa/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » AndroidStudio下NDK开发流程

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址