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);
- 打开 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头文件
(注意):切换到 Android 模式下看到的是 cpp 名称,而非 jni。
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} )
-
配置 gradle,将项目关联到 CMakeLists.txt 文件
同步gradle后生成的目录结构:
生成的gradle,多了以下代码:
一般我们可能只会生成armeabi下的so库,所以我们可以再配置以下配置节:
ndk {
abiFilters 'armeabi', 'x86' // 这里配置 x86 可以供在模拟器下测试使用。
}
```
6.在cpp目录下创建c++文件,编写 file_logger.cpp 文件:
```
#include <jni.h>
#include "com_andon_colbert_ndkdemo_FileLogger.h"
#include <stdio.h>
#include <string.h>
#include <android/log.h>
#include <errno.h>
#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,否则会打开文件失败。