Android NDK c创建新的线程

在jni的c/c++层创建一个新的线程只需要3步:

1.导入库

#include<pthread.h>

2.写好线程要做的事

void* run_1(void*);

void* run_1(void* args){

...

}

3.调用方法

pthread_t thread_1;

pthread_create(&thread_1,NULL,run_1,args);


///

但是这样的线程,缺少了JNIEnv指针,好像干不了什么事,所以就要做这个基础上,得到JNIEnv指针,并将该线程依附于Java虚拟机之上,这样这个线程像java层过来的线程一样,能够干很多事情。


官方文档关于attachCurrentThread()的说明,好像勉强看得懂,就翻译一下试试。。。

The JNI interface pointer (JNIEnv) is valid only in the current thread. Should another thread need to access the Java VM, it must first call AttachCurrentThread() to attach itself to the VM and obtain a JNI interface pointer. Once attached to the VM, a native thread works just like an ordinary Java thread running inside a native method. The native thread remains attached to the VM until it calls DetachCurrentThread() to detach itself.

The attached thread should have enough stack space to perform a reasonable amount of work. The allocation of stack space per thread is operating system-specific. For example, using pthreads, the stack size can be specified in thepthread_attr_t argument to pthread_create.

JNI接口的JNIEnv指针只是在当前线程是有效的(意思是不同的线程不能共用一个JNIEnv*)。其他的线程要访问虚拟机的时候,他就必须通过调用AttachCurrentThread()方法来让当前线程与虚拟机建立某种关系,然后得到一个指针,也就是JNIEnv*。这个线程一旦与了虚拟机建立了某种关系,这个本地(由c/c++代码创建)的线程就能够像一个通常的java线程一样在c/c++层执行了。这种关系一直保持着,直到他调用了DetachCurrentThread()方法来解除关系。

这个与vm保持着某种关系的线程必须要保证有足够的栈空间来执行各种工作。每个线程所能分配的栈空间大小是有明确规定的。举个例子,使用了pthreads,栈的大小就应该是在pthread_create方法调用时传入的pthread_attr_t参数(第二个参数)的大小之内。(这里正好说明了以上方法的第二个参数是干嘛的,null估计就默认分配了)



//

下面代码示例:
一、首先介绍一下最简单的新建一个线程:


java本地类

  1. package com.aii.ndk;  
  2.   
  3. public class NativeThread {  
  4.     static {  
  5.         System.loadLibrary("NativeThreadTest");  
  6.     }  
  7.   
  8.     public native int nativeThread();  
  9. }  
package com.aii.ndk;

public class NativeThread {
	static {
		System.loadLibrary("NativeThreadTest");
	}

	public native int nativeThread();
}

c代码:

  1. #include "com_aii_ndk_NativeThread.h"  
  2. #include <pthread.h>  
  3.   
  4. void *run1(void *);  
  5. /* 
  6.  * Class:     com_aii_ndk_NativeThread 
  7.  * Method:    nativeThread 
  8.  * Signature: ()V 
  9.  */  
  10. JNIEXPORT jint JNICALL Java_com_aii_ndk_NativeThread_nativeThread(JNIEnv *env,  
  11.         jobject thiz) {  
  12.     pthread_t thread1;  
  13.     int a = pthread_create(&thread1, NULL, run1, NULL);  
  14.     return a;  
  15. }  
  16.   
  17. void* run1(void *args) {  
  18.   
  19.     return NULL;  
  20. }  
#include "com_aii_ndk_NativeThread.h"
#include <pthread.h>

void *run1(void *);
/*
 * Class:     com_aii_ndk_NativeThread
 * Method:    nativeThread
 * Signature: ()V
 */
JNIEXPORT jint JNICALL Java_com_aii_ndk_NativeThread_nativeThread(JNIEnv *env,
		jobject thiz) {
	pthread_t thread1;
	int a = pthread_create(&thread1, NULL, run1, NULL);
	return a;
}

void* run1(void *args) {

	return NULL;
}

特别简单的几步,实现了新建一个线程,虽然什么事都没做,但是先缕一缕,因为后面的比较复杂,把后面的看成是在这之上附加的会简单一些。


二、c层一个新的线程,回调java层的方法,来改变UI显示

java native类:

  1. package com.aii.ndk;  
  2.   
  3. public abstract class NativeThread {  
  4.   
  5.     static{  
  6.         System.loadLibrary("NdkLocalThread");  
  7.     }  
  8.     //2.本地方法会产生一个新的线程,然后新的线程来回调这个callBack方法  
  9.     public abstract void callBack();  
  10.     //1.调用这个本地方法  
  11.     public native int startNativeThread();  
  12. }  
package com.aii.ndk;

public abstract class NativeThread {

	static{
		System.loadLibrary("NdkLocalThread");
	}
	//2.本地方法会产生一个新的线程,然后新的线程来回调这个callBack方法
	public abstract void callBack();
	//1.调用这个本地方法
	public native int startNativeThread();
}
c++代码:

  1. #include "com_aii_ndk_NativeThread.h"  
  2. #include <pthread.h>  
  3.   
  4. //全局存放java虚拟机对象  
  5. JavaVM* j_vm;  
  6. //线程对象  
  7. pthread_t thread_1;  
  8. //线程run方法的声明  
  9. void* run1(void*);  
  10. //全局记录回调函数的方法id  
  11. jmethodID callBack_method;  
  12.   
  13. /* 
  14.  * Class:     com_aii_ndk_NativeThread 
  15.  * Method:    startNativeThread 
  16.  * Signature: ()I 
  17.  */JNIEXPORT jint JNICALL Java_com_aii_ndk_NativeThread_startNativeThread(  
  18.         JNIEnv *env, jobject thiz) {  
  19.   
  20.     //先得到回调函数的方法id,记录在全局变量,以便run方法回调  
  21.     jclass clazz = env->GetObjectClass(thiz);  
  22.     callBack_method = env->GetMethodID(clazz, "callBack""()V");  
  23.   
  24.     //得到当前对象(c调用java代码的时候需要用到java对象),将对象传入run方法,以便回调  
  25.     jobject obj = env->NewGlobalRef(thiz);  
  26.     //开启线程,传入run方法方法名,传入参数  
  27.     pthread_create(&thread_1, NULL, run1, obj);  
  28.     return (int) j_vm;  
  29. }  
  30. //线程的run方法  
  31. void *run1(void* args) {  
  32.     //记录从jvm中申请JNIEnv*的状态  
  33.     int status;  
  34.     //用于存放申请过来的JNIEnv*  
  35.     JNIEnv *env;  
  36.     //用于标记线程的状态,用于开启,释放  
  37.     bool isAttached = false;  
  38.     //获取当前状态,查看是否已经拥有过JNIEnv指针  
  39.     status = j_vm->GetEnv((void**) &env, JNI_VERSION_1_4);  
  40.     if (status < 0) {  
  41.         //将当前线程依附于java虚拟机:  
  42.         //这样能够得到一个JNIEnv*指针,  
  43.         //该线程也能够像java线程一样,在一定规则下运行  
  44.         //这个状态将持续直到调用detachCurrentThread方法  
  45.         status = j_vm->AttachCurrentThread(&env, NULL);  
  46.         if (status < 0)  
  47.             return NULL;  
  48.         isAttached = true;  
  49.     }  
  50.     //执行这个线程要做的事情  
  51.     env->CallVoidMethod((jobject) args, callBack_method);  
  52.     //执行完了释放  
  53.     if (isAttached)  
  54.         j_vm->DetachCurrentThread();  
  55.     return NULL;  
  56. }  
  57.   
  58. //以下是onload方法的改动,目的是为了得到vm对象,在新的线程中要用到  
  59. //  
  60.   
  61. //这里设置java中全类名  
  62. static const char *classPathName = "com/aii/ndk/NativeThread";  
  63. //设置java中对应类名中的各种方法的各种属性,以便查找到:  
  64. //这里只有一个方法,所以数组中就装了一个元素:  
  65. //1.字符串类型:java中的方法名  
  66. //2.字符串类型:signature 用来表示方法的参数和返回值类型,在.h的自动生次文档的注释中可找到  
  67. //3.void*类型:c中方法名(也就是上面的方法名)  
  68. static JNINativeMethod methods[] = { { "startNativeThread""()I",  
  69.         (void*) Java_com_aii_ndk_NativeThread_startNativeThread } };  
  70.   
  71. static int registerNativeMethods(JNIEnv* env, const char* className,  
  72.         JNINativeMethod* gMethods, int numMethods) {  
  73.     jclass clazz = env->FindClass(className);  
  74.     env->RegisterNatives(clazz, gMethods, numMethods);  
  75.     return JNI_TRUE;  
  76. }  
  77. static int registerNatives(JNIEnv* env) {  
  78.     return registerNativeMethods(env, classPathName, methods,  
  79.             sizeof(methods) / sizeof(methods[0]));  
  80. }  
  81.   
  82. //这里重写onload方法,这样java中调用"System.loadLibrary("")"的时候就会调用这个onload方法  
  83. //默认情况下调用的是默认的onload方法,  
  84. //要使自己的onload方法被调用到,需要按以下的格式书写方法,其中"JNIEXPORT"可要可不要  
  85. JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {  
  86.     //首先是将vm指针记录在全局变量中,以便线程中要用到  
  87.     j_vm = vm;  
  88.     JNIEnv* env;  
  89.     //分配一个JNIEnv*,存放在env中,之后调用方法的时候要用到  
  90.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)  
  91.         return -1;  
  92.     //通过调用上面的方法,相当于在这个JNIEnv中画了一张对照表,java中要执行什么方法,就到上面去找,找到了后再去调用c中对应的方法  
  93.     if (registerNatives(env) != JNI_TRUE) {  
  94.         return -1;  
  95.     }  
  96.     return JNI_VERSION_1_4;  
  97.   
  98. }  
#include "com_aii_ndk_NativeThread.h"
#include <pthread.h>

//全局存放java虚拟机对象
JavaVM* j_vm;
//线程对象
pthread_t thread_1;
//线程run方法的声明
void* run1(void*);
//全局记录回调函数的方法id
jmethodID callBack_method;

/*
 * Class:     com_aii_ndk_NativeThread
 * Method:    startNativeThread
 * Signature: ()I
 */JNIEXPORT jint JNICALL Java_com_aii_ndk_NativeThread_startNativeThread(
		JNIEnv *env, jobject thiz) {

	//先得到回调函数的方法id,记录在全局变量,以便run方法回调
	jclass clazz = env->GetObjectClass(thiz);
	callBack_method = env->GetMethodID(clazz, "callBack", "()V");

	//得到当前对象(c调用java代码的时候需要用到java对象),将对象传入run方法,以便回调
	jobject obj = env->NewGlobalRef(thiz);
	//开启线程,传入run方法方法名,传入参数
	pthread_create(&thread_1, NULL, run1, obj);
	return (int) j_vm;
}
//线程的run方法
void *run1(void* args) {
	//记录从jvm中申请JNIEnv*的状态
	int status;
	//用于存放申请过来的JNIEnv*
	JNIEnv *env;
	//用于标记线程的状态,用于开启,释放
	bool isAttached = false;
	//获取当前状态,查看是否已经拥有过JNIEnv指针
	status = j_vm->GetEnv((void**) &env, JNI_VERSION_1_4);
	if (status < 0) {
		//将当前线程依附于java虚拟机:
		//这样能够得到一个JNIEnv*指针,
		//该线程也能够像java线程一样,在一定规则下运行
		//这个状态将持续直到调用detachCurrentThread方法
		status = j_vm->AttachCurrentThread(&env, NULL);
		if (status < 0)
			return NULL;
		isAttached = true;
	}
	//执行这个线程要做的事情
	env->CallVoidMethod((jobject) args, callBack_method);
	//执行完了释放
	if (isAttached)
		j_vm->DetachCurrentThread();
	return NULL;
}

//以下是onload方法的改动,目的是为了得到vm对象,在新的线程中要用到
//

//这里设置java中全类名
static const char *classPathName = "com/aii/ndk/NativeThread";
//设置java中对应类名中的各种方法的各种属性,以便查找到:
//这里只有一个方法,所以数组中就装了一个元素:
//1.字符串类型:java中的方法名
//2.字符串类型:signature 用来表示方法的参数和返回值类型,在.h的自动生次文档的注释中可找到
//3.void*类型:c中方法名(也就是上面的方法名)
static JNINativeMethod methods[] = { { "startNativeThread", "()I",
		(void*) Java_com_aii_ndk_NativeThread_startNativeThread } };

static int registerNativeMethods(JNIEnv* env, const char* className,
		JNINativeMethod* gMethods, int numMethods) {
	jclass clazz = env->FindClass(className);
	env->RegisterNatives(clazz, gMethods, numMethods);
	return JNI_TRUE;
}
static int registerNatives(JNIEnv* env) {
	return registerNativeMethods(env, classPathName, methods,
			sizeof(methods) / sizeof(methods[0]));
}

//这里重写onload方法,这样java中调用"System.loadLibrary("")"的时候就会调用这个onload方法
//默认情况下调用的是默认的onload方法,
//要使自己的onload方法被调用到,需要按以下的格式书写方法,其中"JNIEXPORT"可要可不要
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
	//首先是将vm指针记录在全局变量中,以便线程中要用到
	j_vm = vm;
	JNIEnv* env;
	//分配一个JNIEnv*,存放在env中,之后调用方法的时候要用到
	if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
		return -1;
	//通过调用上面的方法,相当于在这个JNIEnv中画了一张对照表,java中要执行什么方法,就到上面去找,找到了后再去调用c中对应的方法
	if (registerNatives(env) != JNI_TRUE) {
		return -1;
	}
	return JNI_VERSION_1_4;

}

顺便贴上MainActivity代码与xml布局文件

  1. package com.aii.ndk;  
  2.   
  3. import android.os.Bundle;  
  4. import android.os.Handler;  
  5. import android.os.Message;  
  6. import android.support.v7.app.ActionBarActivity;  
  7. import android.view.Menu;  
  8. import android.view.MenuItem;  
  9. import android.view.View;  
  10. import android.widget.Button;  
  11.   
  12. public class MainActivity extends ActionBarActivity {  
  13.     NativeThread t = null;  
  14.     Handler handler = new Handler() {  
  15.         public void handleMessage(Message msg) {  
  16.             MainActivity.this.setTitle("callBack changed the title");  
  17.         };  
  18.     };  
  19.   
  20.     @Override  
  21.     protected void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.activity_main);  
  24.         t = new NativeThread() {  
  25.   
  26.             @Override  
  27.             public void callBack() {  
  28.                 // 这个callBack方法由c层的线程来调用,不能改变UI界面,只能通过messag来传递给mq等待UI线程Looper来处理  
  29.                 Message msg = Message.obtain();  
  30.                 handler.sendEmptyMessage(1);  
  31.             }  
  32.   
  33.         };  
  34.     }  
  35.   
  36.     public void click(View view) {  
  37.         Button btn = (Button) findViewById(R.id.button);  
  38.         btn.setText("the jvm address is  " + t.startNativeThread());  
  39.     }  
  40.   
  41.     @Override  
  42.     public boolean onCreateOptionsMenu(Menu menu) {  
  43.         // Inflate the menu; this adds items to the action bar if it is present.  
  44.         getMenuInflater().inflate(R.menu.main, menu);  
  45.         return true;  
  46.     }  
  47.   
  48.     @Override  
  49.     public boolean onOptionsItemSelected(MenuItem item) {  
  50.         // Handle action bar item clicks here. The action bar will  
  51.         // automatically handle clicks on the Home/Up button, so long  
  52.         // as you specify a parent activity in AndroidManifest.xml.  
  53.         int id = item.getItemId();  
  54.         if (id == R.id.action_settings) {  
  55.             return true;  
  56.         }  
  57.         return super.onOptionsItemSelected(item);  
  58.     }  
  59. }  
package com.aii.ndk;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;

public class MainActivity extends ActionBarActivity {
	NativeThread t = null;
	Handler handler = new Handler() {
		public void handleMessage(Message msg) {
			MainActivity.this.setTitle("callBack changed the title");
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		t = new NativeThread() {

			@Override
			public void callBack() {
				// 这个callBack方法由c层的线程来调用,不能改变UI界面,只能通过messag来传递给mq等待UI线程Looper来处理
				Message msg = Message.obtain();
				handler.sendEmptyMessage(1);
			}

		};
	}

	public void click(View view) {
		Button btn = (Button) findViewById(R.id.button);
		btn.setText("the jvm address is  " + t.startNativeThread());
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <Button  
  7.         android:id="@+id/button"  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:onClick="click"  
  11.         android:text="@string/hello_world" />  
  12.   
  13. </RelativeLayout>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="click"
        android:text="@string/hello_world" />

</RelativeLayout>


ndk配置问题可以看这里:

Ndk配置

ndk调用java层代码可以看这里:

c调用java中的方法

顺带Oracle官网的jni说明:

点击打开链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值