本人刚涉足学习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()来输出当前子线程是第几个被启动的线程。完整的内容如下:

按 Ctrl+C 复制代码

#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;
}

按 Ctrl+C 复制代码

最后,编写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 线程不能正常退出的问题的更多相关文章

  1. 解决安卓JNI native 线程不能正常退出问题二

    直面这个解决方法的可以看我转载的博客            https://www.cnblogs.com/Carlsblog/p/9438016.html 本方法是个投机取巧法,不过也解决了不能正常 ...

  2. JNI通过线程c回调java层的函数

    1.参看博客:http://www.jianshu.com/p/e576c7e1c403 Android JNI 篇 - JNI回调的三种方法(精华篇) 2.参看博客: JNI层线程回调Java函数关 ...

  3. Linux多线程(二)(线程等待,退出)

    1. 线程的等待退出 1.1. 等待线程退出 线程从入口点函数自然返回,或者主动调用pthread_exit()函数,都可以让线程正常终止 线程从入口点函数自然返回时,函数返回值可以被其它线程用pth ...

  4. jni和线程

    JNI官方规范中文版——在程序中集成JVM需要注意的JNI特征 翻译 我们已经讨论了JNI在写本地代码和向本地应用程序中集成JVM时的特征.本章接下来的部分分介绍其它的JNI特征. 8.1 JNI和线 ...

  5. monodroid 调用 JNI Native 的一些问题

    在Android版本开发的过程中,需要使用一些用JNI开发的NDK的native库.这里谈一谈踩到的坑,给大家参考. 虽然java的程序我还算熟悉,但是没有了解过 JNI Native 的开发,一般是 ...

  6. c#无限循环线程如何正确退出

    c#无限循环线程如何正确退出 在主程序将要结束时,迅速正确退出无限循环执行的子线程.一般子线程循环执行会有一个指定的周期, 在子线程等待(或者睡眠)时,无法唤醒退出,尤其在执行周期较长时,子线程无法即 ...

  7. 解决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 ...

  8. 解决React Native使用Fetch API请求网络报Network request failed

    问题来源: 1 . 在测试fetch数据请求时,Xcode9.0以上的无法请求https, 需要在Xcode中加载项目后修改Info.plist的相关配置,具体如下参考 问题及解决方法一模一样,不再重 ...

  9. Win64 驱动内核编程-12.回调监控进线程创建和退出

    回调监控进线程创建和退出 两个注册回调的函数:PsSetCreateProcessNotifyRoutine   进程回调PsSetCreateThreadNotifyRoutine    线程回调分 ...

随机推荐

  1. work-7.2

    安装ubuntu,jdk ,git,maven,Intellij. 配置GIT时,需要将在本地生成的公钥粘贴到服务端. 先占个座,具体过程待补充. -------------------------- ...

  2. lua tonumber

    [1]应用tonumber函数 local function test(telnum) , )) == ) -- 5000~5999公司预留号码 , ) == ' or isLen or isRese ...

  3. flask 电子邮件Flask-Mail

    电子邮件 在web程序中,经常会需要发送电子邮件.比如,在用户注册账户时发送确认邮件:定期向用户发送热门内容或是促销信息等等.在Web程序中发送电子邮件并不复杂,借助扩展Flask-Mail或是第三方 ...

  4. Linux笔记 #10# 用于支持Web应用开发&部署&配置的一些自定义脚本

    索引 一.本地开发与测试相关脚本 1.startup.sh 2.shutdown.sh 3.catalina-out.sh 4.localhost_access_log.sh 5.上传本地文件到服务器 ...

  5. Html5 Page Creator,简易h5页面场景制作

  6. 纸小墨ink简洁主题story爱上你的故事

    主题介绍 为纸小墨写的一款主题,该主题移植自Yumoe github地址:ink-theme-story Demo ink-theme-story 主题的一些食用说明 菜单 标题旁边有一个 · 字符, ...

  7. 找出 Xcode 编译C/C++过程文件及生成文件

    在使用 Xcode 编写C/C++时,会发现在项目的目录位置是没有编译过程的那些 .o .exe 文件,只有一个 C/C++ 源代码 .c 文件.如下图(在Mac OS的finder中右键窗口标题名称 ...

  8. springboot2.0 JPA配置自定义repository,并作为基类BaseRepository使用

    springboot2.0 JPA配置自定义repository,并作为基类BaseRepository使用 原文链接:https://www.cnblogs.com/blog5277/p/10661 ...

  9. libc++abi.dylib: terminating with uncaught exception of type NSException (lldb)

    In Xcode 9 and Swift 4: Print exception stack to know the reason of the exception: Go to show break ...

  10. windows 根据端口查看进行PID 并杀掉进程

    1. 首先用netstat -ano | find “端口号”查出进程号 明明有端口号是17568和18892, 如何确定是17568呢 2. takslist 查询当前的进行 3. 如何杀死进程呢  ...