解决JNI native 线程不能正常退出的问题

本文介绍如何在JNI中实现多线程编程,并确保线程能够正常退出。通过具体实例演示了如何从JNI线程回调Java方法,包括创建线程、Attach和Detach线程等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解决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()来输出当前子线程是第几个被启动的线程。完整的内容如下:

 

按 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!!!!

 

posted @ 2018-08-07 16:57 浮躁世界中的一股清流 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值