(三) pthread、ndk错误排查

定位到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会被置空
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值