解决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 线程回调分 ...
随机推荐
- echarts起始角度
series : [ { type: 'pie', startAngle: 180,// minAngle:5,// radius : ['0','60%'], center: ['50%', '50 ...
- mySQL的行转列
因为MYSQL里边没有 PIVOT 现记录: 原表格: mysql语句: SELECT MAX(CASE WHEN corol='红' THEN NUM else 0 END) A ...
- ssh很慢的问题转子
根据网上的解决办法解决了,记录一下: 问题:ssh ***@192.*.*.* ,然后就一直卡在这个地方,很久以后才会出现让输入密码的提示 解决办法:1.关闭防火墙--chkconfig ipta ...
- CCF CSP 201512-1 数位之和
题目链接:http://118.190.20.162/view.page?gpid=T37 问题描述 试题编号: 201512-1 试题名称: 数位之和 时间限制: 1.0s 内存限制: 256.0M ...
- MATLAB 均方根误差MSE、两图像的信噪比SNR、峰值信噪比PSNR、结构相似性SSIM
今天的作业是求两幅图像的MSE.SNR.PSNR.SSIM.代码如下: clc; close all; X = imread('q1.tif');% 读取图像 Y=imread('q2.tif'); ...
- 如何使用mongodb(建立原型,连接数据库)
前两天看了一个朋友做的mongodb数据库,他是自己从某网络大学试听课学的,从可读性和模块区分方面做的比较差,所以写下此文,以作交流. 首先是创建一个modules文件夹,这里面用来存放mongodb ...
- 如何使用postman传数组数据
如何使用postman传数组数据 在我们做api接口数据调试的时候,大部分是会用到postman的,一般请求数据的参数都是字符串,但是特殊情况下我们是需要传一个数组数据的,那么为了实现这种需求,究竟该 ...
- HDFS配置参数及优化之实战经验(Linux hdfs)
HDFS优化之实战经验 Linux系统优化 一.禁止文件系统记录时间 Linux文件系统会记录文件创建.修改和访问操作的时间信息,这在读写操作频繁的应用中将带来不小的性能损失.在挂载文件系统时设置no ...
- java中的构造器
构造器是什么 1.构造器,也称构造方法.构造函数.作用是构造出来一个类的实例,确保对象得到初始化. 2.构造器的格式: 权限修饰符 类名(无参/有参){}. 3.根据有无参数,可分为无参构造 和有参构 ...
- combineReducers
const reactInit = '@@react/Init' const combineReducers = (reducers) => { const finalReducers = {} ...