关于extern "C"
-
如果我们使用c++想要调用c语言写的库中的函数,需要使用
-
c++面向对象,支持重载,会对类进行“名称修饰”。
eg:c++中想要调用c中的add(int a,int b),它可能寻找的函数名称是add_int_int(int,int),不写的话会导致找不到
-
如下:
#ifdef __cplusplus
extern "C" {
#endif
// xxx
#ifdef __cplusplus
}
#endif
关于jnienv
- 它是执行jni函数表的指针,用于jvm跟本地代码之间的通信。
- 每个线程都有自己的jnienv
两个lib的native依赖
可以参考matrix的方式
子线程获取env
// native中用来获取每个线程的JniEnv指针
#include "managed_jnienv.h"
#include <pthread.h>
#include <cstdlib>
namespace JniInvocation {
static JavaVM *g_VM = nullptr;
static pthread_once_t g_onceInitTls = PTHREAD_ONCE_INIT;
static pthread_key_t g_tlsJavaEnv;
void init(JavaVM *vm) {
if (g_VM) return;
g_VM = vm;
}
JavaVM *getJavaVM() {
return g_VM;
}
JNIEnv *getEnv() {
JNIEnv *env;
int ret = g_VM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
if (ret != JNI_OK) {
pthread_once(&g_onceInitTls, []() {
pthread_key_create(&g_tlsJavaEnv, [](void *d) {
if (d && g_VM)
g_VM->DetachCurrentThread();
});
});
if (g_VM->AttachCurrentThread(&env, nullptr) == JNI_OK) {
pthread_setspecific(g_tlsJavaEnv, reinterpret_cast<const void*>(1));
} else {
env = nullptr;
}
}
return env;
}
} // namespace JniInvocation
子线程无法获取应用的类
原因是通过上面的方式获取的env只能加载系统类,不能加载自己写的类。
解决:
定义全局的jclass,在JNI_OnLoad回调中初始化
关于JNI_OnLoad
JNI_OnLoad(JavaVM *vm, void *)
可以理解为jni的入口,java中调用system.loadLibrary("xx")加载so库的时候会调用该方法,可以做几件事
- 保存javaVm方便其他线程使用env
- 注册跟java方法对应的native方法
- 获取子线程需要使用的jclass设置为全局变量(注意释放)
Crash的时候如何定位具体的代码行号
gitbash里面输入:
$ adb logcat | C:/Users/wangzeqi/AppData/Local/Android/Sdk/ndk/21.1.6352462/prebuilt/windows-x86_64/bin/ndk-stack.cmd -sym D:/github/AnrMonitor/lib_anr/build/intermediates/merged_native_libs/debug/out/lib/arm64-v8a
static void *quitSigRunnable(void *args) {
bool fromMyself = *(bool *) args;
if (!fromMyself) {
callBackQuitSigFromOtherProcess();
}
sendQuit2SignalCatcher();
return nullptr;//void * 必须return!!!!否则编译不报错,但是运行的时候会出错!!!!!
}