定位到NDK中出错的代码行数:
.\ndk-bundle\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin\arm-linux-androideabi-addr2line
// 参数:动态库、报错的内存地址(backtrace: #00 pc 00034499 …)
arm-linux-androideabi-addr2line.exe -e .\app\build\intermediates\cmake\debug\obj\x86\libhjMedia.so 00034499 // 内存地址为 backtrace: 后的内存地址
// 输出结果为 .\app\src\main\cpp\ff\jni/pthread.cpp:98
//
//
// @author hankin
// @time 2018/9
//
#include "hjcommon.hpp"
#include <unistd.h>
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 初始化互斥锁,也可用 pthread_mutex_init 函数初始化
static pthread_cond_t cond; // 线程条件
static bool isWait = true;
static void * p1run(void * arg)
{
/*
输出日志为:
p2 start. // 这里等待1秒后才会打印后面的日志
p1 start.
p1 end.
p2 end.
*/
pthread_mutex_lock(&mutex);
LOGD("p1 start.");
isWait = false;
pthread_cond_signal(&cond); // 修改线程条件,让pthread_cond_wait之后的代码继续执行
LOGD("p1 end.");
pthread_mutex_unlock(&mutex);
return 0;
}
static void * p2run(void * arg)
{
while (isWait)
{
pthread_mutex_lock(&mutex);
LOGD("p2 start.");
// pthread_cond_wait 除了被 pthread_cond_t 唤醒外,还有其他可能被唤醒,所以要写在while循环里
pthread_cond_wait(&cond, &mutex); // pthread_cond_wait(阻塞等待pthread_cond_t被唤醒,会unlock互斥,被唤醒后会重新lock互斥,while循环)
LOGD("p2 end.");
pthread_mutex_unlock(&mutex);
}
return 0;
}
JNIEXPORT void JNICALL Java_hankin_hjmedia_ff_some_JNIpthreadActivity_pthreadCond(JNIEnv *env, jobject instance)
{
pthread_t p1, p2;
pthread_attr_t attr;
pthread_cond_init(&cond, 0); // 初始化线程条件
pthread_attr_init(&attr);
// 将线程设置为分离状态,或者PTHREAD_CREATE_JOINABLE正常状态,对于不需要返回值的线程最好设成可分离状态,否则不可分离线程的线程结束后如果没有调用pthread_join函数接收返回值,线程结束后还会在系统里占资源
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&p2, &attr, p2run, 0); // 让 pthread_cond_wait 所在的线程先跑
//pthread_join(p2, 0); // 不分离状态的线程,挂起等到线程执行完毕
sleep(1); // 1秒
pthread_create(&p1, &attr, p1run, 0);
pthread_attr_destroy(&attr);
//pthread_mutex_destroy(&mutex); // 销毁 mutex
//pthread_cond_destroy(&cond); // 销毁 pthread_cond_t
}
static jclass g_clz = 0;
static void * envRun(void * arg)
{
int *ret = new int; // 线程返回值
*ret = 101;
JNIEnv * jnienv = 0; // 每个线程都有一个JNIEnv,可以通过JavaVM来获取
JavaVMAttachArgs args = {JNI_VERSION_1_4, "hj_pthread", 0}; // JavaVMAttachArgs成员:jni版本、线程名称、ThreadGroup
hj_javavm->AttachCurrentThread(&jnienv, &args); // 通过JavaVM关联当前线程,获取当前线程的JNIEnv , args参数可以不设置
jmethodID mid = jnienv->GetStaticMethodID(g_clz, "getStr", "()Ljava/lang/String;"); // 获取类的静态函数
jstring jstr = (jstring) jnienv->CallStaticObjectMethod(g_clz, mid); // 调用静态函数,返回值为String
char str[128];
hjcpyJstr2char(jnienv, jstr, str);
LOGD("jni getStr=%s", str); // jni getStr=java_str
hj_javavm->DetachCurrentThread(); // 解除关联
return (void *)ret;
}
static int num = 0;
JNIEXPORT void JNICALL Java_hankin_hjmedia_ff_some_JNIpthreadActivity_pthreadJNIEnv(JNIEnv *env, jobject instance) // 每个线程都有一个JNIEnv,可以通过JavaVM来获取
{
JavaVM * vm = 0;
env->GetJavaVM(&vm); // 除了在 JNI_OnLoad 函数中获取 JavaVM 外,还能用此方式获取 JavaVM
LOGD("JNI_OnLoad vm=%x, env->GetJavaVM=%x", hj_javavm, vm); // JNI_OnLoad vm=a4b03820, env->GetJavaVM=a4b03820 地址是一样的
jclass clz = env->FindClass("hankin/hjmedia/ff/some/JNIpthreadActivity"); // 获取java类 , 在子线程中用子线程的JNIEnv查找类会找不到。。
// jni中的对象如果要以全局变量的方式来跨线程使用,需要通过全局引用来设置,正常的变量赋值的方式是不可以的(会报 use of deleted local reference ) (同线程可以这样)
g_clz = (jclass) env->NewGlobalRef(clz); // 创建一个 jclass 的全局引用
pthread_t thrd;
pthread_create(&thrd, 0, envRun, 0);
int * ret = 0;
pthread_join(thrd, (void **)&ret);
LOGD("pthread ret=%d", *ret); // pthread ret=101
delete ret;
int a = 10 / num;
LOGD("%d", a);
env->DeleteGlobalRef(g_clz); // 删除全局引用,g_clz会被置空
}