Android(安卓)NDK c创建新的线程
在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本地类
[java] view plain copy print ?- package com.aii.ndk;
- public class NativeThread {
- static {
- System.loadLibrary("NativeThreadTest");
- }
- public native int nativeThread();
- }
package com.aii.ndk;public class NativeThread {static {System.loadLibrary("NativeThreadTest");}public native int nativeThread();}
c代码: [cpp] view plain copy print ?
- #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;
- }
#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();
- }
package com.aii.ndk;public abstract class NativeThread {static{System.loadLibrary("NdkLocalThread");}//2.本地方法会产生一个新的线程,然后新的线程来回调这个callBack方法public abstract void callBack();//1.调用这个本地方法public native int startNativeThread();}
c++代码: [cpp] view plain copy print ? - #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;
- }
#include "com_aii_ndk_NativeThread.h"#include //全局存放java虚拟机对象JavaVM* j_vm;//线程对象pthread_t thread_1;//线程run方法的声明void* run1(void*);//全局记录回调函数的方法idjmethodID 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布局文件 [java] view plain copy print ?
- 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);
- }
- }
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");};};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);t = new NativeThread() {@Overridepublic 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());}@Overridepublic 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;}@Overridepublic 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);}}
[html] view plain copy print ?
- <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说明:
点击打开链接
更多相关文章
- android 通用混淆配置(近期)
- Android实现KSOAP2访问WebService
- ANDROID中根据QQ号码或者QQ群号码,跳转到指定的QQ号码聊天或者QQ
- Android(安卓)动态申请存储权限
- SharedPreferences存储数据的使用方法(转)
- Android获取系统储存以及内存信息的方法(二)
- Android之使用MediaMetadataRetriever类获取视频第一帧
- Android(安卓)后台线程弹对话框导致程序崩溃(is not valid; is y
- Android(安卓)创建fragment时向fragment中传数据