Android下的JNI创建多线程的方法

20 篇文章 0 订阅
11 篇文章 0 订阅

本文参考了http://www.cnblogs.com/lknlfy/archive/2012/03/16/2400786.html这篇博文,加了点自己的东西

废话不多说,贴代码上来

java的代码:

package com.example.jni_thread_demo;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;

public class JNI_ThreadActivity extends Activity {
	private Button mButton = null;
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_jni__thread);
		
		mButton = (Button)findViewById(R.id.button);
		mButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// 调用JNI中的函数来启动JNI中的线程
				mainThread();
			}
		});
		
		// 初始化JNI环境
		setJNIEnv();
		
	}
	
	//由JNI中的线程回调类方法
	private static void fromJNI(int i)
	{
		Log.v("Java---------->", ""+i);
	}
       //自己定义的线程回调成员方法
	private void From_JNI_Again (int i)
	{
		Log.v("Java_object------------>", ""+i);
	}
	
	// 本地方法
	private native void mainThread();
	private native void setJNIEnv();
	
	static
	{
		System.loadLibrary("JNIThread");
		
	}
}

jni中的代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<assert.h>

#include<jni.h>
#include<android/log.h>


#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "jni_thread", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "jni_thread", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "jni_thread", __VA_ARGS__))

// 线程数
#define NUMTHREADS 5
// 指定要注册的类

#define JNIREG_CLASS "com/example/jni_thread_demo/JNI_ThreadActivity"
// 全局变量
JavaVM* g_jvm = NULL;
jobject g_obj = NULL;

void* thread_fun(void* arg)
{
	JNIEnv *env;
	jclass cls;
	jmethodID mid, mid1;

	// 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("GetStaticMethodID() Error ......");
		goto error;
	}
	// 最后调用java中的静态方法
	(*env)->CallStaticVoidMethod(env, cls, mid, (int)arg);

	//获得类中的“成员”方法
	mid1 = (*env)->GetMethodID(env, cls, "From_JNI_Again", "(I)V");
	if(mid == NULL)
	{
		LOGE("GetMethodID() Error ......");
		goto error;
	}
	// 最后调用类中“成员”方法
	(*env)->CallVoidMethod(env, g_obj, mid1, (int)arg);

	//错误处理代码
	error:
	//Detach主线程
	if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
	{
		LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
	}

	pthread_exit(0);
}

/*
 * Class:     com_example_jni_thread_demo_JNI_ThreadActivity
 * Method:    mainThread
 * Signature: ()V
 */
/*不用JNI_OnLoad时的复杂命名方式
JNIEXPORT void JNICALL Java_com_example_jni_1thread_1demo_JNI_1ThreadActivity_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);
	}
}*/

/*
 * Class:     com_example_jni_thread_demo_JNI_ThreadActivity
 * Method:    setJNIEnv
 * Signature: ()V
 *//*不用JNI_OnLoad时的复杂命名方式
JNIEXPORT void JNICALL Java_com_example_jni_1thread_1demo_JNI_1ThreadActivity_setJNIEnv
  (JNIEnv *env, jobject obj)
{
	// 保存全局JVM以便在子线程中使用
	(*env)->GetJavaVM(env, &g_jvm);
	// 不能直接赋值(g_obj = ojb)
	g_obj = (*env)->NewGlobalRef(env, obj);
}*/

JNIEXPORT void JNICALL native_mainThread(JNIEnv *env, /*jclass clazz*/ jobject obj)// 使用jclass和jobject都可以
{
	LOGI("native_mainThread");
	int i;
	pthread_t pt[NUMTHREADS];

	for(i=0; i<NUMTHREADS; i++)
	{
		// 创建线程,并指明调用的函数,注意只接收一个参数i作为thread_fun的参数,后面会介绍怎么传多个参数
		pthread_create(&pt[i], NULL, &thread_fun, (void*)i);
	}
}

JNIEXPORT void JNICALL native_setJNIEnv(JNIEnv *env, /*jclass obj*/ jobject obj)// 使用jclass和jobject都可以
{
	LOGI("native_setJNIEnv");
	// 保存全局JVM以便在子线程中使用
	(*env)->GetJavaVM(env, &g_jvm);
	// 不能直接赋值(g_obj = ojb)
	g_obj = (*env)->NewGlobalRef(env, obj);
}

/**
* Table of methods associated with a single class.
*/
static JNINativeMethod gMethods[] =
{
		{"mainThread", "()V", (void*)native_mainThread },	// 绑定:注意千万签名结尾不能加分号!!!!!!
		{"setJNIEnv", "()V", (void*)native_setJNIEnv },
};

/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
	JNINativeMethod* gMethods, int numMethods)
{
	jclass clazz;
	clazz = (*env)->FindClass(env, className);
	if (clazz == NULL)
	{
		return JNI_FALSE;
	}
	if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
	{
		return JNI_FALSE;
	}

	return JNI_TRUE;
}

/*
* Register native methods for all classes we know about.
*/
static int registerNatives(JNIEnv* env)
{
	if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
			sizeof(gMethods) / sizeof(gMethods[0])))
	return JNI_FALSE;

	return JNI_TRUE;
}

/*
* Set some test stuff up.
*
* Returns the JNI version on success, -1 on failure.
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
	JNIEnv* env = NULL;
	jint result = -1;

	if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
		LOGE("GetEnv failed!");
		return -1;
	}
	//===========================================
	assert(env != NULL);

	if (!registerNatives(env))
	{// 注册本地方法
		return -1;
	}
	//===========================================
	/* success -- return valid version number */
	result = JNI_VERSION_1_4;

	return result;
}

Android.mk文件中的代码:

LOCAL_PATH :=$(call my-dir)

include $(CLEAR_VAR)

LOCAL_MODULE := JNIThread
LOCAL_SRC_FILES := JNI_Thread.c
LOCAL_MODULE_FILENAME := libJNIThread//如果报LOCAL_MODULE_FILENAME的错的话,需要加上这句话
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

如何给线程函数传递多个参数:

涉及多参数传递给线程的,都需要使用结构体将参数封装后,将结构体指针传给线程
定义一个结构体

struct mypara
{
  var para1;//参数1
  var para2;//参数2
}


将这个结构体指针,作为void *形参的实际参数传递
struct mypara pstru;
pthread_create(&ntid, NULL, thr_fn,& (pstru));


函数中需要定义一个mypara类型的结构指针来引用这个参数
void *thr_fn(void *arg)
{
  mypara *pstru;
  pstru = (* struct mypara) arg;
  pstru->para1;//参数1
  pstru->para2;//参数2	
}

如何多线程同步通信:

至于多个线程是怎么进行同步通信的,可以参考这篇博文

http://blog.csdn.net/chenghongyue/article/details/8124976



  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Chap1:JNI完全手册... 3 Chap2:JNI-百度百科... 11 Chap 3:javah命令帮助信息... 16 Chap 4:用javah产生一个.h文件... 17 Chap5:jni教程(very very good) 19 Chap6: JNI传递返回值... 26 15.2.2.3 传递字符串... 28 15.2.2.4 传递整型数组... 29 15.2.2.5 传递字符串数组... 30 15.2.2.6 传递对象数组... 31 Chap7:Jni中C++和Java的参数传递... 33 Chap8:如何将java传递过来的jbyteArray转换成C/C++中的BYTE数组... 47 Chap5:使用JNI技术实现java程序调用第三方dll(c/c++)文件的功能... 47 Chap9:如何编写jni方法(转载)... 55 1、实例一:在jni中调用标准c中自带的函数printf(): 57 2、实例二、调用c 语言用户定义的函数... 58 3、实例三、在jni函数中访问java类中的对象实例域... 58 4、实例四:在jni函数中访问类的静态实例域... 60 5、实例五:在jni函数中调用java对象的方法... 60 6、实例六:在jni函数中调用java类的静态方法... 61 7、实例七:jni函数中传递基本数据类型参数... 62 8、实例八:在jni函数中传递对象类型参数... 62 9、实例九:在jni函数中处理字符串... 63 10、实例十:在jni函数中处理数组... 64 11、实例十一:在jni中的返回值问题... 65 12、实例十二:在jni创建java类对象:... 66 Chap10:在 Windows 中实现 Java 本地方法... 66 1.Java 调用 C. 67 2.调试... 76 3.其他信息... 79 Chap11:如何在C/C++中调用Java. 80 1.环境搭建... 81 2.初始化虚拟机... 83 3.访问类方法... 85 4访问类属性... 87 5.访问构造函数... 88 6.数组处理... 89 7.中文处理... 89 8.异常... 91 9.线程和同步访问... 91 10.时间... 92 Chap12:基本JNI调用技术(c/c++与java互调) 93 Chap13:JNI的c代码中,另外一个线程获取 JNIEnv. 96 chap 14:当JNI遇到多线程--java对象如何被C++中的多个线程访问?. 97 chap 15:JNI多线程中的应用... 101 chap 16:JNI限制(多线程)... 105 chap 17:使用 Java Native Interface 的最佳实践... 106 1.性能缺陷... 107 2.正确性缺陷... 117 3.避免常见缺陷... 121 4.结束语... 128 Chap18:JNI设计实践之路... 129 一、 前言... 129 二、 JNI基础知识简介... 130 三、 Java程序调用非Java程序... 131 四、 C/C++访问Java成员变量和成员方法... 138 五、 异常处理... 140 六、 MFC程序中嵌入Java虚拟机... 142 Chap19:JNI编程系列之基础篇... 148 System.loadLibrary("HelloWorld"); 149 JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject); 150 Chap20:JNI编程系列之中级篇(上)... 151 1. Java基本类型的传递... 151 2. String参数的传递... 151 3. 数组类型的传递... 153 4. 二维数组和String数组... 154 Chap21:JNI编程系列之高级篇... 155 1. 在一般的Java类中定义native方法... 156 2. 访问Java类的域和方法... 156 3. 在native方法中使用用户定义的类... 157 4. 异常处理... 158
Android 是Google开发的基于Linux平台的开源手机操作系统。它包括操作系统、用户界面和应用程序—— 移动电话工作所需的全部软件,而且不存在任何以往阻碍移动产业创新的专有权障碍。 Android以Java为编程语言,使接口到功能,都有层出不穷的变化,其中Activity等同于J2ME的MIDlet,一个 Activity 类(class)负责创建视窗(window),一个活动中的Activity就是在 foreground(前景)模式,背景运行的程序叫做Service。两者之间通过由ServiceConnection和AIDL连结,达到复数程序同时运行的效果。 Android基础阶段:平台架构特性(JAVA/C) Market/应用程序组件 环境搭建与部署/打包与发布 AVD/DDMS/AAPT 调试与测试 相关资源访问/资源制作 Activity/Service/Broadcast Receiver/Content Provider/原理(生命周期)及深层实现. Android进阶初级:组件Widget/ 菜单Menu/ 布局Layout 详解 Xml解析(Pull/Dom/Sax)/JNI 解析SQL数据库原理, SQLit e /SharedPreferences/File详解 多媒体Audio/Video/Camera 详解 Android进阶高级:蓝牙/WIFI SMS/MMS 应用实现 深层次解析GPS原理,实现LocationManager/LocationProvider 进行定位/跟踪/查找/趋近警告以及Geocoder正逆向编解码等技术细节 2D图形库(Graphics/View)详解 SDCARD/传感器/手势 应用实现 《老罗Android视频教程(第一季)》的视频一共分为15个章节, 涵盖了Android入门、常用UI布局介绍、HTTP编程、 XML与Json数据解析、JDBC与Web编程、常用UI控件、 数据储存与文件操作、对话框、通知、菜单、 LoaderManager异步加载、多线程(AsyncTask与Handler)、 百度地图等十五个模块,一共102集。 本网盘分享章节编号是按照视频更新的先后顺序编号的,具体学习可参考如下章节顺序: 【第一版第一章】老罗Android开发视频--入门介绍(9集) 【第一版第二章】老罗Android开发视频--常用UI布局介绍(5集) 【第一版第三章】老罗Android开发视频--HTTP协议编程(4集) 【第一版第四章】老罗Android开发视频--解析XML数据(3集) 【第一版第五章】老罗Android开发视频--解析JSON数据(4集) 【第一版第六章】老罗Android开发视频--服务器端JDBC编程(2集) 【第一版第七章】老罗Android开发视频--服务器端Web编程(6集) 【第一版第八章】老罗Android开发视频-常用UI控件(33集) 【第一版第九章】老罗Android开发视频--存储数据和文件(7集) 【第一版第十章】老罗Android开发视频--对话框介绍(4集) 【第一版第十一章】老罗Android开发视频--通知的使用(2集) 【第一版第十二章】老罗Android开发视频--菜单的使用(4集) 【第一版第十三章】老罗Android开发视频--异步加载数据库(2集) 【第一版第十四章】老罗Android开发视频--多线程编程(7集) 【第一版第十五章】老罗Android开发视频--百度地图实战开发(10集)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShaderJoy

您的打赏是我继续写博客的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值