在jni的c/c++层创建一个新的线程只需要3步:
1.导入库
#include
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
.
这个与vm保持着某种关系的线程必须要保证有足够的栈空间来执行各种工作。每个线程所能分配的栈空间大小是有明确规定的。举个例子,使用了pthreads,栈的大小就应该是在pthread_create方法调用时传入的pthread_attr_t参数(第二个参数)的大小之内。(这里正好说明了以上方法的第二个参数是干嘛的,null估计就默认分配了)
//////////////////////////////////////////////////////
下面代码示例:
一、首先介绍一下最简单的新建一个线程:
java本地类
package com.aii.ndk;
public class NativeThread {
static {
System.loadLibrary("NativeThreadTest");
}
public native int nativeThread();
}
#include "com_aii_ndk_NativeThread.h"
#include
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类:
package com.aii.ndk;
public abstract class NativeThread {
static{
System.loadLibrary("NdkLocalThread");
}
//2.本地方法会产生一个新的线程,然后新的线程来回调这个callBack方法
public abstract void callBack();
//1.调用这个本地方法
public native int startNativeThread();
}
c++代码:
#include "com_aii_ndk_NativeThread.h"
#include
//全局存放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;
}
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);
}
}
ndk配置问题可以看这里:
Ndk配置
ndk调用java层代码可以看这里:
c调用java中的方法
顺带oracle官网的jni说明:
点击打开链接