文章目录

  • I . JNI 线程创建
  • II . 线程执行函数
  • III . 线程方法获取 Java 对象
  • IV . 线程方法获取 JNIEnv
  • V . JNI 线程 完整代码示例

I . JNI 线程创建


1. 线程创建方法函数原型 :

int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, (void*)(*start_rtn)(void*), void *arg)`;

2. pthread_create 方法的 4 个参数 ;

  • 参数 1 ( pthread_t *tidp ) : 线程标识符指针 , 该指针指向线程标识符 ;
  • 参数 2 ( const pthread_attr_t *attr ) : 线程属性指针 ;
  • 参数 3 ( (void*)(*start_rtn)(void*) ) : 线程运行函数指针 , start_rtn 是一个函数指针 , 其参数和返回值类型是 void* 类型 ;
  • 参数 4 ( void *arg ) : 参数 3 中的线程运行函数的参数 ;

3. 返回值说明 :

  • 线程创建成功 , 返回 0 ;
  • 线程创建失败 , 返回 错误代码 ;

4. 关于函数指针参数的说明 : C++ 中函数指针类型是 void *(PTW32_CDECL *start) (void *)

  • 函数的参数类型是 void* 指针 ;
  • 函数的返回值类型 void* 指针 ;

5. 函数多参数方案 : 如果线程执行的函数有多个参数 , 可以使用结构体 , 类进行封装 ;

6. 线程属性 : 创建线程时 , 给线程指定属性 pthread_attr_t 是结构体类型 ;

7. 代码示例 :

 /*线程创建方法函数原型 : int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, (void*)(*start_rtn)(void*), void *arg);该方法需要提供四个参数 ;参数 1 ( pthread_t *tidp ) :线程标识符指针 , 该指针指向线程标识符 ;参数 2 ( const pthread_attr_t *attr ) : 线程属性指针 ;参数 3 ( (void*)(*start_rtn)(void*) ) : 线程运行函数指针 , start_rtn 是一个函数指针 , 其参数和返回值类型是 void* 类型参数 4 ( void *arg ) : 参数 3 中的线程运行函数的参数 ;返回值 :线程创建成功 , 返回 0 ;线程创建失败 , 返回 错误代码 ;关于函数指针参数 : C++ 中函数指针类型是 void *(PTW32_CDECL *start) (void *) ,函数的参数类型是 void* 指针函数的返回值类型 void* 指针函数多参数方案 : 如果线程执行的函数有多个参数 , 可以使用结构体 , 类进行封装线程属性 : 创建线程时 , 给线程指定属性 pthread_attr_t 是结构体类型*///函数指针 函数名 和 &函数名 都可以作为函数指针pthread_create( &pid , 0 , threadRun, 0 );

II . 线程执行函数


1. 线程执行函数的要求 : C++ 中规定线程执行函数的函数指针类型是 void *(PTW32_CDECL *start) (void *) ;

2. 函数作用 : 将该函数的指针作为线程创建方法 pthread_create 的第三个参数 ;

3. 参数处理 : 在线程创建时 , 传入参数 , 将该参数转为 char* 字符串指针类型 , 将其打印出来 ;

4. 代码示例 :

/*定义线程中要执行的方法将该函数的指针作为线程创建方法 pthread_create 的第三个参数C++ 中规定线程执行函数的函数指针类型是 void *(PTW32_CDECL *start) (void *)
*/
void* pthread_function(void* args) {//延迟 100 ms 执行//_sleep(100);//指针类型转换 : 将 void* 转为 char*//   使用 static_cast 类型转换标识符char* hello = static_cast<char*>(args);//打印参数cout << "pthread_function 线程方法 执行 参数 : " << hello << endl;return 0;
}

III . 线程方法获取 Java 对象


线程方法获取 Java 对象步骤 :

① 定义全局变量 jobject obj : 使用该全局变量存储 Java 对象 ;

//JNI 方法参数中的第二个参数 , 需要先将局部变量转为全局变量 , 然后再其它方法中调用
jobject obj;

② JNI 方法处理 : 将 jobject instance 参数 ( 此时是局部变量 ) 转为 全局变量 , 调用 NewGlobalRef 方法实现 ;

void threadDemoC(JNIEnv *env, jobject instance){__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "threadDemoC");//保存全局变量 , 先将局部变量转为全局变量 , 然后再赋值给全局的 obj 变量//  使用域作用符访问全局的 ::obj 变量::obj = env->NewGlobalRef(instance);...
}

这样就可以在其它方法或其它线程中使用该 Java 对象了 ;

IV . 线程方法获取 JNIEnv


线程中获取 JNIEnv * env 步骤 :

① JNIEnv 无法跨线程 : JNI 方法参数中的 JNIEnv 指针是不能跨线程使用的 , 在 主线程中调用 JNI 方法 , 其 JNIEnv 指针不能在子线程中使用 ;

② 获取途径 : 如果在子线程中使用 JNIEnv 指针 , 需要使用 JavaVM 获取 指定线程的 JNIEnv 指针 ;

③ 绑定线程 : 调用 JavaVM 的 AttachCurrentThread 方法 , 可以绑定线程 , 其传入一个 JNIEnv ** 二维指针 , 会返回该线程对应的 JNIEnv 指针 ;

④ 剥离线程 : 注意使用完 JNIEnv 后 , 解绑线程 , 调用 JavaVM 的 DetachCurrentThread 方法 解绑线程 ;

2 . 代码示例 :

/*线程执行的方法如果在 Native 层执行耗时操作 , 如下载文件 , 需要在线程中处理JNI 方法参数中的 JNIEnv 指针是不能跨线程使用的 , 在 主线程中调用 JNI 方法 , 其 JNIEnv 指针不能在子线程中使用如果在子线程中使用 JNIEnv 指针 , 需要使用 JavaVM 获取 指定线程的 JNIEnv 指针调用 JavaVM 的 AttachCurrentThread 可以获取本线程的 JNIEnv 指针注意最后还要将线程从 Java 虚拟机中剥离关于参数传递 :传递 int 类型  和 int * 类型 , 传递指针可以在 方法中修改 int 变量值 ;传递 int * 类型 和 int ** 类型 , 传递二维指针 可以在方法中修改 int * 一维指针值因此有些参数需要在方法中修改, 并且需要保存该修改状态 , 就需要将该变量的地址当做参数传入原来的普通变量 变成 指针变量 , 一维指针 变 二维指针*/
void* threadRun(void *args){__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "threadRun");//JNIEnv 不能跨线程使用 , 这里需要先获取本线程的 JNIEnvJNIEnv *env;//将线程附加到 Java 虚拟机中 ( 注意后面对应剥离线程操作 )//  如果成功返回 0 , 如果失败 , 直接退出int attachResult = _vm->AttachCurrentThread(&env, 0);...//将线程从 Java 虚拟机中剥离_vm->DetachCurrentThread();//注意这里一定要返回 0 , 否则执行到结尾会崩溃return 0;}

V . JNI 线程 完整代码示例


1 . Java 层代码 :

package kim.hsl.thread;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);threadDemoJava();}/*** JNI 线程 Demo*/public native void threadDemoJava();/*** 打印当前线程信息*/public void logThread(){Log.i("JNI_TAG", Thread.currentThread().toString());}}

2 . Native 层代码 :

#include <jni.h>
#include <string>
#include <android/log.h>//导入线程头文件
#include <pthread.h>//Java 虚拟机指针 , 在 JNI_OnLoad 方法中设置该值
JavaVM *_vm;//JNI 方法参数中的第二个参数 , 需要先将局部变量转为全局变量 , 然后再其它方法中调用
jobject obj;/*线程执行的方法如果在 Native 层执行耗时操作 , 如下载文件 , 需要在线程中处理JNI 方法参数中的 JNIEnv 指针是不能跨线程使用的 , 在 主线程中调用 JNI 方法 , 其 JNIEnv 指针不能在子线程中使用如果在子线程中使用 JNIEnv 指针 , 需要使用 JavaVM 获取 指定线程的 JNIEnv 指针调用 JavaVM 的 AttachCurrentThread 可以获取本线程的 JNIEnv 指针注意最后还要将线程从 Java 虚拟机中剥离关于参数传递 :传递 int 类型  和 int * 类型 , 传递指针可以在 方法中修改 int 变量值 ;传递 int * 类型 和 int ** 类型 , 传递二维指针 可以在方法中修改 int * 一维指针值因此有些参数需要在方法中修改, 并且需要保存该修改状态 , 就需要将该变量的地址当做参数传入原来的普通变量 变成 指针变量 , 一维指针 变 二维指针
*/
void* threadRun(void *args){__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "threadRun");//JNIEnv 不能跨线程使用 , 这里需要先获取本线程的 JNIEnvJNIEnv *env;//将线程附加到 Java 虚拟机中 ( 注意后面对应剥离线程操作 )//  如果成功返回 0 , 如果失败 , 直接退出int attachResult = _vm->AttachCurrentThread(&env, 0);//获取 MainActivity 对应的 jclass 对象jclass clazz = env->GetObjectClass( obj );//反射获取 logThread 方法jmethodID logThreadID = env->GetMethodID(clazz, "logThread", "()V");//调用 logThread 方法env->CallVoidMethod(obj, logThreadID);//释放相关的局部变量env->DeleteLocalRef(clazz);//将线程从 Java 虚拟机中剥离_vm->DetachCurrentThread();//注意这里一定要返回 0 , 否则执行到结尾会崩溃return 0;}void threadDemoC(JNIEnv *env, jobject instance){__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "threadDemoC");//保存全局变量 , 先将局部变量转为全局变量 , 然后再赋值给全局的 obj 变量//  使用域作用符访问全局的 ::obj 变量::obj = env->NewGlobalRef(instance);//代表一个线程的句柄pthread_t pid;//创建线程并执行pthread_create( &pid , 0 , threadRun, 0 );
}//下面的代码是动态注册内容static const JNINativeMethod methods[] = {{"threadDemoJava", "()V", (void *)threadDemoC}
};static const char* className = "kim/hsl/thread/MainActivity";int JNI_OnLoad(JavaVM *vm , void* reserved){// 1 . 记录 Java 虚拟机指针_vm = vm;// 2 . 动态注册方法//获取 JNIEnv 指针JNIEnv *env = nullptr;int registerResult = vm->GetEnv( (void **) &env, JNI_VERSION_1_6 );if(registerResult != JNI_OK){return -1;}//进行动态注册jclass jclazz = env->FindClass(className);env->RegisterNatives(jclazz, methods, sizeof(methods) / sizeof(JNINativeMethod));return JNI_VERSION_1_6;
}

3 . 执行结果 :

2020-02-08 23:47:58.253 25293-25293/? I/JNI_TAG: threadDemoC
2020-02-08 23:47:58.253 25293-25316/? I/JNI_TAG: threadRun
2020-02-08 23:47:58.253 25293-25316/? I/JNI_TAG: Thread[Thread-2,10,main]

【Android NDK 开发】JNI 线程 ( JNI 线程创建 | 线程执行函数 | 非 JNI 方法获取 JNIEnv 与 Java 对象 | 线程获取 JNIEnv | 全局变量设置 )相关推荐

  1. Android NDK开发之旅(2):一篇文章搞定Android Studio中使用CMake进行NDK/JNI开发

    Android NDK开发之旅(2):一篇文章搞定android Studio中使用CMake进行NDK/JNI开发 (码字不易,转载请声明出处:http://blog.csdn.NET/andrex ...

  2. 【Android NDK 开发】JNI 方法解析 ( JNIEnv *env 参数 )

    文章目录 一. JNI 方法解析 二. JNIEnv *env 参数解析 三. C 语言 环境中 JNIEnv *env 参数解析 四. C ++ 环境中 JNIEnv *env 参数解析 总结 : ...

  3. 安卓逆向_15( 三 ) --- Android NDK 开发【 jni 静态注册、JNI_OnLoad 动态注册】

    Android Studio开发JNI示例:https://blog.csdn.net/wzhseu/article/details/79683045 JNI_动态注册_静态注册.zip : http ...

  4. Android NDK开发之旅(3): 详解JNI数据类型与C/C++、Java之间的互调

    Android NDK开发之旅(3):详解JNI数据类型与C/C++.Java之间的互调 (码字不易,转载请声明出处:http://blog.csdn.net/andrexpert/article/d ...

  5. Android NDK开发Crash错误定位

    在Android开发中,程序Crash分三种情况:未捕获的异常.ANR(Application Not Responding)和闪退(NDK引发错误).其中未捕获的异常根据logcat打印的堆栈信息很 ...

  6. 【Android NDK 开发】NDK 交叉编译 ( Ubuntu 中交叉编译动态库 | Android Studio 中配置使用第三方动态库 )

    文章目录 I . 动态库 与 静态库 II . 编译动态库 III. Android Studio 使用第三方动态库 IV . Android Studio 关键代码 V . 博客资源 I . 动态库 ...

  7. OpenCV android sdk配置OpenCV android NDK开发实例

    OpenCV android sdk配置OpenCV android NDK开发实例 [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article/det ...

  8. Android NDK开发从0到1

    本文的开发环境为 Windows,其他平台操作类似 其实说到 NDK 就不得不提 JNI ( Java Native Interface ) ,JNI 是专门用来与本地代码进行交互而提供的一个接口.通 ...

  9. Android NDK开发一 NDK环境搭建及cmake简介

    1 前言 关于NDK的介绍可以查看官方的介绍: https://developer.android.com/ndk/guides/index.html 一句话总结NDK:NDK(Native Deve ...

最新文章

  1. iOS -- UIApplication
  2. 中兴智能视觉大数据报:要注意,人工智能将衍生更多工作机会
  3. AWS Elastic Block Store和Simple Storage Services区别
  4. WebService 基础
  5. javascript基本功
  6. eclipse中查看mysql_eclipse中怎样查看sqlite数据库的表
  7. 《机器学习实战》笔记(02):k-近邻算法
  8. 天然气门站监控摄像头如何布置_监控摄像头布置原则
  9. JSP学习之include指令
  10. Spring : 缓存相关注解@EnableCaching、@CacheConfig、@Cacheable、@Caching
  11. QT中webkit去掉默认的右键菜单
  12. 【数据分享】历次人口普查数据(一普到七普)
  13. 实时环境映射贴图(Real-time Evironmnet Mapping)
  14. VIM教程与学习资料汇总(转载自善用佳软)
  15. HTTP传输大文件的方法
  16. 【爬虫+数据可视化】Python爬取CSDN博客访问量数据并绘制成柱状图
  17. MIT新任女校长震撼北美高校圈!61岁的她曾是杜克首位女教务长
  18. 【5月比赛合集】80场可报名的数据挖掘大奖赛,任君挑选!
  19. android手机电视互动,安卓手机投屏酷开电视如何实现 多屏互动这么玩
  20. 基于随机森林模型的葡萄酒品质分析

热门文章

  1. Ubuntu ADSL 拨号上网时断时续问题
  2. T^TOJ - 1251 - 。◕‿◕。TMD - 欧拉函数 - 质因数分解
  3. 使用属性升级MyBank
  4. 团队项目-个人博客-4.20
  5. C: Answers to “The C programming language, Edition 2”
  6. JSF框架在NetBeans下的编码
  7. Linux思维导图之shell脚本编程基础、习题
  8. jquery对输入框内容的数字校验
  9. .NET获取机器信息
  10. 第二冲刺站立会议01