根据日常学习持续更新中
message([
Record the specified message text in the log. If more than one message string is given, they are concatenated into a single message with no separator between the strings.
mode参数可以有不同的选项,一般不会选择ERROR级别,ERROR会停止cmake运行
cmake_message
如果要在日志中打印变量的值的话可以使用${}在引号中包裹变量
message(CHECK_FAIL “missing components: ${variable}”)
message(WARNING " add definition ")
参考:cmake_message
add_definitions(-DDEBUG) 是定义宏-D后面是宏的名称,在c++代码中我们可以使用ifdef DEBUG 来使用我们的编译参数
if ("${variable}" STREQUAL "true"){
}else{
}
获取编译参数重传递到cmake的值,然后比较字符串然后进行判断
//cmake 的参数配置入口
externalNativeBuild {
cmake {
// 指定一些编译选项
cppFlags "-std=c++11 -frtti -fexceptions"
//如何向变量传递参数,对应的格式如下(arguments "-D变量名=参数")
arguments '-DANDROID_PLATFORM=android-24', '-DANDROID_STL=c++_static', '-DANDROID_STL=c++_shared'
// 也可以使用下面这种语法向每个变量传递多个参数(参数之间使用空格隔开),格式如下
// arguments "-D变量名=参数1 参数2"
arguments "-DANDROID_CPP_FEATURES=rtti exceptions"
}
}
参考:
https://blog.csdn.net/ljx1400052550/article/details/117280541
set_source_files_properties(file1.cpp PROPERTIES COMPILE_FLAGS "-std=c++14")
例如我们可以使用上面的配置让单个文件使用c14去编译。
set_source_files_properties
在方法参数前添加native关键字
例如:
public native String get();
javah 输入命令的目录需要是包名的根目录,也就是需要包含包名
终端路径:/Users/lxd/code/Android/lxdAndroidStart/app/src/main/java
命令:javah com.example.androidstart.JniTest
const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
int len = env->GetStringUTFLength(jstr);
printf("from java str=%s, len=%d", str, len);
env->ReleaseStringUTFChars(jstr, str);
const char *str = "hello, world";
return env->NewStringUTF(str);
方法签名生成:
javap
-s 输出内部类型签名
传入-s后面的参数需要是classes,可以通过javac获取
javac 编译java文件生成class文件/或者可以去项目编译中的中间产物中去寻找class文件
c++ lambda
Lambda表达式完整的声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }
链接:
https://www.cnblogs.com/DswCnblog/p/5629165.html
JavaVM
JavaVM再Android中只有一个,JavaVM带有函数表,允许你创建和销毁JavaVM。
JNIEnv
JNIEnv提供了大多数的JNI函数,对于C语言的代码,本地函数都需要接收JNIEnv为第一个参数,而对于C++,JNIEnv不需要作为参数传入
JNIEnv用做线程私有存储,因此,不能在线程间共享JNIEnv变量,如果一个代码块没有JNIEnv,可以通过JavaVM去获取
在jni.h的定义中,针对c++和c的不同,有着不同的定义,因此两种语言混用的时候需要注意。
java中字符串使用的是UTF-16编码,
JNI中使用 utf-8 表示字符串,UTF-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节;
c/c++使用的是原始数据,ascii就是一个字节了,中文一般是GB2312编码,用两个字节来表示一个汉字。
所以三种类型的字符串如果含有中文的时候需要特殊转换下
首先我们在java中使用native关键字声明这个方法是native方法,然后使用静态注册或者动态注册,将native方法和c++实现绑定
public native void nativeStaticRegister();
生成native方法对应的c++头文件
使用javah生成class文件对应的头文件,-d 第一个参数是输出路径,第二个参数是src目录下的类的全名
在对应的Terminal路径输入命令,我的路径是这个/Users/XXX/code/Android/NativeJni/app/src/main/java
javah -d …/cpp/ com.example.nativejni.CallBackClass
输入了上面的命令后就会在 cpp 目录下生成对应的cpp头文件
直接cpp文件中输入native方法名,as会提示回车后自动补全
或者我们将公共部分提出来,写成一个宏,然后使用宏
#define FFMPEG_FUNC(RETURN_TYPE, FUNC_NAME, ...) \
JNIEXPORT RETURN_TYPE JNICALL Java_com_example_nativejni_MainActivity_##FUNC_NAME \
(JNIEnv *env, jclass thiz, ##__VA_ARGS__)
动态注册我们在JNI_OnLoad方法中使用RegisterNatives进行注册,将java的native方法和c++进行绑定。
因为绑定的时候需要字节码的方法签名:
获取方法签名的方式
c++中jni的方法前都有个这个关键字,
不带extern c编译出来的so中的符号
_Z46Java_com_example_nativejni_MainActivity_getvalP7_JNIEnvP8_jobject
带extern c
Java_com_example_nativejni_MainActivity_getval
带extern c编译出来的符号才符合jni命名,extern c让编译器使用c的编译规则编译指定代码
查看so中符号的方法:
在我们的ndk目录下,比如我的路径是
ndk/21.4.7075529/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/
aarch64-linux-android-nm
在这个terminal执行 ./aarch64-linux-android-nm so路径
就会展示出so的符号列表(对于debug包apk中解压出来的so自己试了下需要加上-D参数才能显示动态链接符号)
./aarch64-linux-android-nm --help 查看-D参数的含义
-D, --dynamic Display dynamic symbols instead of normal symbols
参考:https://blog.csdn.net/sinat_36817189/article/details/110423243
STATIC静态库:变异的时候会将程序和静态库进行链接,可执行程序中会包含当前的静态库,多个可执行程序会有多份静态库。
SHARED动态库:动态库的调用和链接是在运行时,可执行程序中并不包含动态库,多个可执行程序共享一份动态库
当-fvisibility=hidden时动态库中的函数默认是被隐藏的即 hidden. 除非显示声明为__attribute__((visibility("default")))
.
当-fvisibility=default时动态库中的函数默认是可见的.除非显示声明为__attribute__((visibility("hidden")))
if (env->ExceptionOccurred()) {
LOGI("occurred lxd exception");
env->ExceptionClear();
}
链接:
java JNI官方教程
ndk-stask查看崩溃堆栈
$NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi-v7a -dump foo.txt
上面的foo.txt指的是崩溃的堆栈,可以从崩溃的日志中拷贝出来,要从*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***开始拷贝,要包含这个
./ndk-stack --help
usage: ndk-stack.py [-h] -sym SYMBOL_DIR [-i INPUT]
Symbolizes Android crashes.
optional arguments:
-h, --help show this help message and exit
-sym SYMBOL_DIR, --sym SYMBOL_DIR
directory containing unstripped .so files
-i INPUT, -dump INPUT, --dump INPUT
input filename
See .
上面的-sym传入的SYMBOL_DIR要求是unstripped,unstripped是啥意思呢
在我们编译so生成的产物下面,cmake的产物没有strip,so会大很多
striped目录下面会有去处符号的so,体积会小很多
/Users/lxd/Library/Android/sdk/ndk/21.4.7075529/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin
strip工具目录
链接:
官方ndk-stack使用教程
https://blog.csdn.net/yangzex/article/details/126581161
addr2line查看代码位置
// 0x12345678为堆栈地址,替换为实际崩溃地址我们可以查看到我们的代码崩溃的位置
aarch64-linux-android-addr2line -e libxxx.so 0x12345678
readelf -d libxxx.so查看其依赖库:
./aarch64-linux-android-readelf --help
-d --dynamic Display the dynamic section (if present)
-s --syms Display the symbol table(查看符号表)
objdump 反汇编so文件
./arm-linux-androideabi-objdump –S libxx.so
参考:
https://blog.csdn.net/u011686167/article/details/124132719