您当前的位置: 首页 >  c++

命运之手

暂无认证

  • 1浏览

    0关注

    747博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【NDK】【018】NDK使用Logcat控制台,JString和C++标准库

命运之手 发布时间:2020-03-04 00:21:28 ,浏览量:1

注意事项

当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             
关注
打赏
1654938663
查看更多评论
0.0423s