注意事项
当mk文件中一个选项包含多个值时,可以通过空格割开,或者+=来拼接
#可以是这种形式
LOCAL_SRC_FILES += logcat.cpp com_easing_android_JniHello.cpp
#也可以是这种形式
LOCAL_SRC_FILES += logcat.cpp
LOCAL_SRC_FILES += com_easing_android_JniHello.cpp
千万不能这么写,这相当于给变量连续赋值两次,会覆盖之前的变量值
LOCAL_SRC_FILES := logcat.cpp
LOCAL_SRC_FILES := com_easing_android_JniHello.cpp
使用Logcat控制台
在Android.mk中增加以下配置,链接安卓Logcat库
LOCAL_LDLIBS := -lm -llog
在Cpp中封装以下方法,来调用安卓中的Log类
#include
#include
#include
#define LOG_TAG "JniHello"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
__VA_ARGS__就是我们传入的参数列表,比如我们传入了arg1和arg2两个参数,那么就相当于
char* arg1 = "Hello";
char* arg2 = "JNI";
//以下两行代码是等价的
//LOGI只允许接收一个char*型参数,这两行代码是错误的,只是为了介绍语法规则
LOGI(arg1, arg2);
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, arg1, arg2);
使用JString
我们在Java中,很少会使用char[],一般都会使用String 而在C++端,经常用到的字符串类型都是char*,比如上面的LOGI方法 String在C++中对应这JString类型,所以我们一般需要将JString转成char*来使用
JNIEnv *env = nullptr;
const char *str = env->GetStringUTFChars(message, nullptr);
LOGI(str);
利用JNIEnv可以很容易地实现这个转换,但值得注意的是,由于这个转换并不是严格安全的,所以NDK默认编译是无法通过的,必须关闭NDK的安全选项才能编译,需要执行以下两步操作
找到ndk-bundle/build/core/default-build-commands.mk文件,找到并修改以下配置
#TARGET_FORMAT_STRING_CFLAGS := -Wformat -Werror=format-security
TARGET_FORMAT_STRING_CFLAGS := -Wformat
在Application.mk中添加以下配置
APP_CPPFLAGS += -Wno-error=format-security
使用C++标准库
现代的C++开发基本上离不开std标准库,但是std并不是C++的内置语法,只是一套地位接近内置语法的通用基础库 我们在使用NDK手,必须手动修改编译配置,链接到std标准库,才能使用iostream,cout,string等功能,这需要执行以下两步操作
Application.mk中增加以下配置
APP_STL := c++_static
Android.mk中增加以下配置
LOCAL_C_INCLUDES := D:/dev/sdk/ndk/21.0.6113669/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a
将std::cout重定向到Logcat控制台
通过以上配置,我们就可以使用std::cout等标准库对象了 但是安卓应用并不是控制台程序,std::cout打印的内容是不可见的,我们必须将信息打印到Logcat控制台中才有意义 解决方法是,将std::cout重定向到一个FileDescription,这样std::cout输出的内容就会写到FileDescription里面,我们再开一个线程,将FileDescription里面的内容取出来,打印到Logcat控制台即可
我们编写一个logcat.cpp文件,既封装了Log方法,又实现了std::cout的重定向功能 这样我们以后就可以在JNI代码中,随意使用Log和std::cout了,非常方便
//logcat.cpp
#ifndef LOGCAT_CPP
#define LOGCAT_CPP
#include
#include
#include
#include
#include
#include
static int pfd[2];
static pthread_t tid;
static char *TAG = "JniLog";
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
//子线程,不断从FileDescription中读取数据并打印
static void *threadFunc(void *) {
ssize_t byteCount;
char buffer[1024];
while ((byteCount = read(pfd[0], buffer, sizeof buffer - 1)) > 0) {
if (buffer[byteCount - 1] == '\n') --byteCount;
buffer[byteCount] = 0;
LOGI(buffer);
}
return 0;
}
//开启JNI日志服务
static int startNativeLogger(char *tag) {
TAG = tag;
//将stdout与stderror与缓冲区解绑
setvbuf(stdout, nullptr, _IOLBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
//打开FileDescription,并将stdout和stderror重定向到FileDescription
pipe(pfd);
dup2(pfd[1], 1);
dup2(pfd[1], 2);
//开启子线程,不断从FileDescription中读取数据并打印
if (pthread_create(&tid, 0, threadFunc, 0) == -1)
return -1;
pthread_detach(tid);
return 0;
}
#endif
//com_easing_android_JniHello.cpp
#include
#include
//使用std::cout前需先调用此方法,设置打印标志,开启打印服务
extern "C" JNIEXPORT void JNICALL Java_com_easing_android_JniHello_startNativeLogger(JNIEnv *env, jobject obj, jstring tag){
startNativeLogger("JniHello");
}
extern "C" JNIEXPORT void JNICALL Java_com_easing_android_JniHello_hello(JNIEnv *env, jobject obj) {
std::cout
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?