zoukankan      html  css  js  c++  java
  • NDK学习笔记-JNI多线程

    前面讲到记录到ffmpeg音视频解码的时候,采用的是在主线程中进行操作,这样是不行的,在学习了POSIX多线程操作以后,就可以实现其在子线程中解码了,也可以实现音视频同步了

    简单示例

    在native实现中,直接调用pthread的多线程方法,这样就可以在JNI层实现多线程,下面是一个简单的实现多线程的例子

    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <android/log.h>
    
    #include "com_cj5785_posixtest_PosixThread.h"
    
    #define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO, "cj5785",FORMAT,##__VA_ARGS__);
    #define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR, "cj5785",FORMAT,##__VA_ARGS__);
    
    void* thr_func(void *arg)
    {
    	char *buf = (char *)arg;
    	int i = 0;
    	for (i = 0; i < 10; ++i) {
    		LOGI("thread %s --- %d", buf, i);
    		if(i == 8)
    		{
    			pthread_exit((void *)-1);
    		}
    		sleep(1);
    	}
    	return (void *)0;
    }
    
    JNIEXPORT void JNICALL Java_com_cj5785_posixtest_PosixThread_pthread
      (JNIEnv *env, jobject jobj)
    {
    	//创建多线程
    	pthread_t tid;
    	pthread_create(&tid, NULL, thr_func, (void *)"thread-0");
    }
    

    运行apk发现,主线程并未被阻塞,多线程开启成功

    关于JNIEnv与JavaVM

    • 每个线程都会有自己独立的JNIEnv,因此不建议将主线程的JNIEnv传入到子线程中
      而是应该在子线程中获取到自己的JNIEnv

    • 在Android中,只有一个JavaVM对象

    • 那么就可以通过JavaVM获取到每一个线程关联的JNIEnv
      在JNI中,有两种方式可以得到JavaVM

      • JNI_OnLoad函数中获取,JNI_OnLoad在动态库加载时候就会执行,可以在JNI中实现JNI_OnLoad方法,可以得到验证
      JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
      	LOGI("%s","JNI_OnLoad run");
      	return JNI_VERSION_1_4;
      }
      
      • (*env)->GetJavaVM(env,&javaVM);,这种方法在创建子线程中调用即可
    • 在得到javaVM以后,就可以将javaVM关联当前线程,就可以获取到当前线程的JNIEnv

    (*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
    
    • 在使用完毕以后必须解除关联
    (*javaVM)->DetachCurrentThread(javaVM);
    

    注意:这里有一个大坑,JNIEnv不可作为全局变量保存,而应该使用NewGlobalRef()来新建一个全局引用
    Native层本地多线程回调Java函数时env->findClass()失败,这里是由于在子线程中不可以找不到类,这个动作需要在主线程中完成

    NDK调试

    • 首先拿到出错地址,在:项目/obj/local/armeadi目录下
      adb logcat | ndk-stack -sym 链接库地址本地位置
      这时候定位到第一个地址,这个地址就是出错地址
    • 使用ndk下的编译链工具查找出错的行数
      adb logcat | arm-linux-androideabi-addr2line -e 链接库地址本地位置+so

    完成以上两步就能够定位出错的位置了

    通过JNI拿到Java生成的UUID示例

    在Java中生成UUID

    import java.util.UUID;
    public class UUIDUtils {
    	public static String getUUID() {
    		return UUID.randomUUID().toString();
    	}
    }
    

    native方法类中的方法

    public native void init();
    public native void pthread();
    public native void destory();
    

    在native实现中得到UUID值

    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <android/log.h>
    
    #include "com_cj5785_posixtest_PosixThread.h"
    
    #define LOGI(FORMAT,...) __android_log_print(4, "cj5785",FORMAT,##__VA_ARGS__);
    #define LOGE(FORMAT,...) __android_log_print(6, "cj5785",FORMAT,##__VA_ARGS__);
    
    JavaVM *javaVM = NULL;
    jclass uuidutils_cls_global = NULL;
    jmethodID uuidutils_mid = NULL;
    
    //动态库加载时会执行
    JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
    	LOGI("%s","JNI_OnLoad");
    	//javaVM = vm;
    	return JNI_VERSION_1_4;
    }
    
    void* thr_func(void *arg)
    {
    	JNIEnv *env = NULL;
    	//关联
    	(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
    	char *buf = (char *)arg;
    	int i = 0;
    	for (i = 0; i < 10; ++i) {
    		LOGI("%s --- %d", buf, i);
    		jobject uuid = (*env)->CallStaticObjectMethod(env, uuidutils_cls_global, uuidutils_mid);
    		const char *uuid_cstr = (*env)->GetStringUTFChars(env, uuid, NULL);
    		LOGI("%s", uuid_cstr);
    		(*env)->ReleaseStringUTFChars(env, uuid, uuid_cstr);
    	}
    	//解除关联
    	(*javaVM)->DetachCurrentThread(javaVM);
    	pthread_exit((void *)0);
    }
    
    //初始化
    JNIEXPORT void JNICALL Java_com_cj5785_posixtest_PosixThread_init
      (JNIEnv *env, jobject jobj)
    {
    	//在主线程中获取class以及methodID
    	jclass uuidutils_jcls = (*env)->FindClass(env, "com/cj5785/posixtest/UUIDUtils");
    	uuidutils_cls_global = (*env)->NewGlobalRef(env, uuidutils_jcls);
    	uuidutils_mid = (*env)->GetStaticMethodID(env, uuidutils_jcls, "getUUID", "()Ljava/lang/String;");
    }
    
    JNIEXPORT void JNICALL Java_com_cj5785_posixtest_PosixThread_pthread
      (JNIEnv *env, jobject jobj)
    {
    	(*env)->GetJavaVM(env, &javaVM);
    	//创建多线程
    	pthread_t tid;
    	pthread_create(&tid, NULL, thr_func, (void *)"thread-0");
    }
    
    //销毁
    JNIEXPORT void JNICALL Java_com_cj5785_posixtest_PosixThread_destory
      (JNIEnv *env, jobject jobj)
    {
    	(*env)->DeleteGlobalRef(env, uuidutils_cls_global);
    }
    
  • 相关阅读:
    sql -- 获取商品分类的最新销售情况
    sql -- 获取连续签到的用户列表
    sql -- 利用order by 排名作弊
    sql -- update表子查询、多条件判断case when
    sql-- 找到重复数据并删除、有重复数据不插入或更新的处理方法
    sql--自链接(推荐人)
    sql--测试商品的重要度,是否需要及时补货
    sql面试题
    TCP/IP 3次握手
    REST和SOAP
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664647.html
Copyright © 2011-2022 走看看