Hello NDK!

NDK 开发变得越来越重要,接下来介绍一下 Android Studio 下 NDK 开发环境配置:
功能需求:1.打开文件写入日志,2.读取日志的内容

1.创建一个项目,在项目的创建一个 FileLogger.java,里面有下面两个函数:

static {
        // 加载的动态库.so文件,注意这里不用加lib前缀,系统会默认添加
        System.loadLibrary("file_logger");
    }

    /**
     * 写入 Log 到文件
     * @param log_path     日志文件的路径
     * @param message     消息内容
     */
    public static native void writeLogFile(String log_path, String message);


    /**
     * 读取日志文件
     * @param log_path  日志文件的路径
     * @return 读取 Log 文件的内容
     */
    public static native String readLogFile(String log_path);
  1. 打开 Terminal 控制台,生成 JNI 的头文件:
// 进入java的目录
cd app/src/main/java
// 利用javac创建头文件
javah com.andon.colbert.ndkdemo.FileLogger

3.创建 JNI 目录:

app -> New -> Folder -> JNI Folder 

在创建的 jni 目录下,创建 include 目录存放生成的JNI头文件

Hello NDK!_第1张图片
Paste_Image.png

(注意):切换到 Android 模式下看到的是 cpp 名称,而非 jni。

Paste_Image.png

4.在app根目录下创建 CMakeLists.txt

add_library( # 模块的名称.
            file_logger

            # 设置模块为共享库.
            SHARED

            # 指定模块的源文件.
            src/main/jni/file_logger.cpp )

# 指定源文件引用头文件的搜索目录
include_directories(src/main/jni/include/)

# 指定NDK变量引用NDK的模块,储存在NDK中
find_library( 
             # NDK 的变量名
             log-lib

             # 引用的NDK模块名称,这里引用了 android 的 log 库
             log )

# 链接模块到一个或多个其他链接库上
target_link_libraries( # 链接的目标模块
                      file_logger

                      # 这里指定了将 android 的 log库 链接到 file_logger 上
                      ${log-lib} )
  1. 配置 gradle,将项目关联到 CMakeLists.txt 文件


    屏幕快照 2017-05-07 上午1.51.03.png

    Hello NDK!_第2张图片
    屏幕快照 2017-05-07 上午1.53.37.png

    同步gradle后生成的目录结构:


    Hello NDK!_第3张图片
    屏幕快照 2017-05-07 上午1.55.07.png

    生成的gradle,多了以下代码:
Hello NDK!_第4张图片
屏幕快照 2017-05-07 上午1.56.29.png

一般我们可能只会生成armeabi下的so库,所以我们可以再配置以下配置节:

ndk {
    abiFilters 'armeabi', 'x86' // 这里配置 x86 可以供在模拟器下测试使用。
}
```

6.在cpp目录下创建c++文件,编写 file_logger.cpp 文件:
```
#include 
#include "com_andon_colbert_ndkdemo_FileLogger.h"
#include 
#include 
#include 
#include 

#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO, "file_logger", __VA_ARGS__)
#define LOG_E(...) __android_log_print(ANDROID_LOG_ERROR, "file_logger", __VA_ARGS__)

JNIEXPORT void JNICALL Java_com_andon_colbert_ndkdemo_FileLogger_writeLogFile
        (JNIEnv *env, jclass type, jstring log_path, jstring message) {
    const char *path = env->GetStringUTFChars(log_path, 0);
    FILE *log_file = fopen(path, "w+");
    env->ReleaseStringUTFChars(log_path, path);
    if (log_file == NULL) {
        LOG_E("日志文件打开失败!errno: %d", errno);
        return;
    }
    const char *content = env->GetStringUTFChars(message, 0);
    const size_t count = env->GetStringUTFLength(message);
    env->ReleaseStringUTFChars(message, content);
    // 写入文件
    size_t w_count = fwrite(content, sizeof(char), count, log_file);
    if (w_count == count) {
        LOG_I("日志写入成功!");
    }
    // 关闭文件
    fclose(log_file);
}

JNIEXPORT jstring JNICALL Java_com_andon_colbert_ndkdemo_FileLogger_readLogFile
        (JNIEnv *env, jclass type, jstring log_path) {
    const char *path = env->GetStringUTFChars(log_path, 0);
    FILE *log_file = fopen(path, "r");
    env->ReleaseStringUTFChars(log_path, path);
    if (log_file == NULL) {
        LOG_E("日志文件打开失败!errno: %d", errno);
        return NULL;
    }
    // 将文件指针指向文件的末尾
    fseek(log_file , 0 , SEEK_END);
    // 获取当前指针的位置,就是文件的大小
    size_t file_size = ftell(log_file);
    if(file_size == 0L) {
        LOG_E("日志文件为空!");
        fclose(log_file);
        return NULL;
    }
    // 分配内存空间
    char* content = (char*) malloc(file_size);
    if(content == NULL) {
        LOG_E("内存空间不足!");
        fclose(log_file);
        return NULL;
    }
    // 将文件指针指向文件的头部
    fseek(log_file, 0, SEEK_SET);
   // 读取文件的全部内容
    fread(content, sizeof(char), file_size, log_file);
    content[file_size] = '\0'; // 追加字符串结束符。
    fclose(log_file);
    jstring message = env->NewStringUTF(content);
    free(content);
    return message;
}
```

7.在androidTest中编写测试文件:
```
@Test
public void testFileLogger() {
    String message = "Hello NDK!";
    File dir = new File(Environment.getExternalStorageDirectory(), "demo");
    if (!dir.exists()) {
         dir.mkdirs();
    }
    String path = dir.getAbsolutePath() + "/log.txt";
    FileLogger.writeLogFile(path, message);
    String content = FileLogger.readLogFile(path);
    assertEquals(message, content);
}
```
至此,Hello NDK! 就写完了,别忘了加文件读写权限哦,编译测试时,为了方便我把 targetSdkVersion 设置成了22,否则会打开文件失败。

你可能感兴趣的:(Hello NDK!)