解决JNI native 线程不能正常退出的问题
本人刚涉足学习C++ 安卓 java,遇到这个棘手的问题,多谢博客园作者lknlfy 看了你的博客解决了这个问题,此文转发, 方便日后学习
以下内容转自lknlfy作者博客 传送门:http://www.cnblogs.com/lknlfy/archive/2012/03/16/2400786.html
====================================================分界线===============================================================================
一、概述
JNI编程和Linux上的C/C++编程还是挺相似的,每次java调用JNI中的函数时都会传入有关JVM的一些参数(如JNIEnv,jobject),每次JNI回调java中的方法时都要通过JVM的有关参数来实现,当在JNI中涉及到多线程的话还是有一些不一样的地方,就是要在子线程函数里使用AttachCurrentThread()和DetachCurrentThread()这两个函数,在这两个函数之间加入回调java方法所需要的代码。
二、要求
掌握JNI多线程编程的方法。
三、实现
新建工程MyThread,修改main.xml文件,在里面只有一个Button,如下:

1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent"
5 android:orientation="vertical" >
6
7 <Button
8 android:id="@+id/button"
9 android:layout_width="fill_parent"
10 android:layout_height="wrap_content"
11 android:text="启动JNI线程"
12 />
13
14 </LinearLayout>

修改MyThreadActivity.java文件,实现按钮的监听,在里面调用JNI中的函数来启动JNI中的线程,比较简单,如下:

1 package com.nan.thread;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5 import android.util.Log;
6 import android.view.View;
7 import android.widget.Button;
8
9 public class MyThreadActivity extends Activity
10 {
11 private Button mButton = null;
12
13 /** Called when the activity is first created. */
14 @Override
15 public void onCreate(Bundle savedInstanceState)
16 {
17 super.onCreate(savedInstanceState);
18 setContentView(R.layout.main);
19
20 mButton = (Button)this.findViewById(R.id.button);
21 //按钮监听
22 mButton.setOnClickListener(new View.OnClickListener()
23 {
24
25 @Override
26 public void onClick(View v)
27 {
28 // TODO Auto-generated method stub
29 //调用JNI中的函数来启动JNI中的线程
30 mainThread();
31
32 }
33 });
34 //初始化JNI环境
35 setJNIEnv();
36 }
37
38 //由JNI中的线程回调
39 private static void fromJNI(int i)
40 {
41 Log.v("Java------>", ""+i);
42 }
43
44 //本地方法
45 private native void mainThread();
46 private native void setJNIEnv();
47
48 static
49 {
50 //加载动态库
51 System.loadLibrary("JNIThreads");
52 }
53
54 }

编写JNI_Thread.c文件,在mainThread()函数里启动5个子线程,在子线程函数里回调java中的静态方法fromJNI()来输出当前子线程是第几个被启动的线程。完整的内容如下:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<jni.h>
#include<android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-activity", __VA_ARGS__))
//线程数
#define NUMTHREADS 5
//全局变量
JavaVM *g_jvm = NULL;
jobject g_obj = NULL;
void *thread_fun(void* arg)
{
JNIEnv *env;
jclass cls;
jmethodID mid;
//Attach主线程
if((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK)
{
LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
return NULL;
}
//找到对应的类
cls = (*env)->GetObjectClass(env,g_obj);
if(cls == NULL)
{
LOGE("FindClass() Error.....");
goto error;
}
//再获得类中的方法
mid = (*env)->GetStaticMethodID(env, cls, "fromJNI", "(I)V");
if (mid == NULL)
{
LOGE("GetMethodID() Error.....");
goto error;
}
//最后调用java中的静态方法
(*env)->CallStaticVoidMethod(env, cls, mid ,(int)arg);
error:
//Detach主线程
if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
{
LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
}
pthread_exit(0);
}
//由java调用以创建子线程
JNIEXPORT void Java_com_nan_thread_MyThreadActivity_mainThread( JNIEnv* env, jobject obj)
{
int i;
pthread_t pt[NUMTHREADS];
for (i = 0; i < NUMTHREADS; i++)
//创建子线程
pthread_create(&pt[i], NULL, &thread_fun, (void *)i);
}
//由java调用来建立JNI环境
JNIEXPORT void Java_com_nan_thread_MyThreadActivity_setJNIEnv( JNIEnv* env, jobject obj)
{
//保存全局JVM以便在子线程中使用
(*env)->GetJavaVM(env,&g_jvm);
//不能直接赋值(g_obj = obj)
g_obj = (*env)->NewGlobalRef(env,obj);
}
//当动态库被加载时这个函数被系统调用
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env = NULL;
jint result = -1;
//获取JNI版本
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
{
LOGE("GetEnv failed!");
return result;
}
return JNI_VERSION_1_4;
}
最后,编写Android.mk文件:

1 LOCAL_PATH := $(call my-dir)
2
3 include $(CLEAR_VARS)
4
5 LOCAL_MODULE := JNIThreads
6 LOCAL_SRC_FILES := JNI_Threads.c
7
8 LOCAL_LDLIBS := -llog
9
10 include $(BUILD_SHARED_LIBRARY)

使用ndk-build成功编译出动态库后,运行该程序:

并点击2下按钮,LogCat输出如下:

====================================================分界线===============================================================================
Mark Mark!!!!
解决JNI native 线程不能正常退出的问题的更多相关文章
- 解决安卓JNI native 线程不能正常退出问题二
直面这个解决方法的可以看我转载的博客 https://www.cnblogs.com/Carlsblog/p/9438016.html 本方法是个投机取巧法,不过也解决了不能正常 ...
- JNI通过线程c回调java层的函数
1.参看博客:http://www.jianshu.com/p/e576c7e1c403 Android JNI 篇 - JNI回调的三种方法(精华篇) 2.参看博客: JNI层线程回调Java函数关 ...
- Linux多线程(二)(线程等待,退出)
1. 线程的等待退出 1.1. 等待线程退出 线程从入口点函数自然返回,或者主动调用pthread_exit()函数,都可以让线程正常终止 线程从入口点函数自然返回时,函数返回值可以被其它线程用pth ...
- jni和线程
JNI官方规范中文版——在程序中集成JVM需要注意的JNI特征 翻译 我们已经讨论了JNI在写本地代码和向本地应用程序中集成JVM时的特征.本章接下来的部分分介绍其它的JNI特征. 8.1 JNI和线 ...
- monodroid 调用 JNI Native 的一些问题
在Android版本开发的过程中,需要使用一些用JNI开发的NDK的native库.这里谈一谈踩到的坑,给大家参考. 虽然java的程序我还算熟悉,但是没有了解过 JNI Native 的开发,一般是 ...
- c#无限循环线程如何正确退出
c#无限循环线程如何正确退出 在主程序将要结束时,迅速正确退出无限循环执行的子线程.一般子线程循环执行会有一个指定的周期, 在子线程等待(或者睡眠)时,无法唤醒退出,尤其在执行周期较长时,子线程无法即 ...
- 解决React Native unable to load script from assets index.android.bundle on windows
React Native运行的时候,经常碰到React Native unable to load script from assets index.android.bundle on windows ...
- 解决React Native使用Fetch API请求网络报Network request failed
问题来源: 1 . 在测试fetch数据请求时,Xcode9.0以上的无法请求https, 需要在Xcode中加载项目后修改Info.plist的相关配置,具体如下参考 问题及解决方法一模一样,不再重 ...
- Win64 驱动内核编程-12.回调监控进线程创建和退出
回调监控进线程创建和退出 两个注册回调的函数:PsSetCreateProcessNotifyRoutine 进程回调PsSetCreateThreadNotifyRoutine 线程回调分 ...
随机推荐
- ORACLE中 大量数据插入表 SQL
declare g_commit_count number; cursor cu1 is select gl_flexfields_pkg.get_description_sql(gcc.chart_ ...
- Delphi下MSMQ(Mircosoft Message Queue)实例(私有队列)
网上关于消息队列技术原理说明的详细文档很多,但涉及到Delphi的具体实现很少,这是我从网上找了一上午的资料,自己整合和尝试的能运行的程序. 打开控制面板->程序->添加组件,添加消息队列 ...
- loadrunner12中JavaVuser脚本的编写
1.环境准备: 友情提示:用本地环境,不要用虚拟机 LoadRunner11----->对应JDK1.6版本(32位) LoadRunner12----->对应JDK1 ...
- sql sugar
事务 using (var db = new SqlSugarClient(new ConnectionConfig() { ConnectionString = Config.xxx, DbType ...
- 用Springboot实现文件下载功能
ApiOperation(value = "下载文件", httpMethod = "GET", notes = "downloadFile" ...
- Dubbo的异常处理
记一次Dubbo的异常处理过程. 现象:业务团队报送,服务端定义一个BuinessException,继承与RunTimeException,服务端执行时抛出该异常,但是客户端捕捉不到该异常. 记录: ...
- Django的安装和一些操作
1.安装 (1) 命令行: pip install django==1.11.18 pip install django==1.11.18 -i 源 (2) pycharm setting —> ...
- Github-记账本
- centos7.4卸载再安装mariadb服务无法启动问题
今天yum安装MariaDB完成后,启动服务时一直报以下错误 Job for mariadb.service failed. See ‘systemctl status mariadb.service ...
- redis注册为window服务
打开redis安装路径并执行:redis-server --service-install redis.windows.conf 安装服务命令 执行完命令后打开电脑服务 此时服务还未启动,执行命令:r ...