JNI基础

AS新建C++工程
【JNI基础】在新建项目的面板,勾选Include C++ support,新建的项目自动包含C++配置。
C++生成Java中定义的native方法
新生成的项目已包含C++生成Java的native方法范本,新建的native方法,只需使用AS快捷键Alt+Enter自动生成。如:

public native String stringFromJNI();

在c++中注册的方法为:
extern "C" JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }

C++生成的方法为:extern "C" JNIEXPORT {返回类型} JNICALL {方法名} {方法体}
方法名对应格式为:Java_全路径名类名_方法名
参数必须包含两个默认参数JNIEnv *env,jobject instance
若Java方法包含参数,则c++方法在上面两个参数后面新增对应类型的参数
C++中获取方法中的参数
获取Java参数
  • 获取int和String参数
native String test0(int i,String s);

extern "C" JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MainActivity_test0(JNIEnv *env, jobject instance, jint i, jstring s_) { LOGE("获取java中int数据:%d",i); //获取java的String const char* s = env->GetStringUTFChars(s_,0); LOGE("获取java中String数据:%s",s); env->ReleaseStringUTFChars(s_, s); return env->NewStringUTF(s); }

  • 获取int数组和String数组参数
native void test1(int[] i,String[] j);

extern "C" JNIEXPORT void JNICALL Java_com_example_myjnitest_MainActivity_test1(JNIEnv *env, jobject instance, jintArray i_, jobjectArray j) { //遍历int数组 jint *i = env->GetIntArrayElements(i_,NULL); int32_t length = env->GetArrayLength(i_); for (int k = 0; k < length; ++k) { LOGE("遍历java的int数组:%d",*(i+ k)); } env->ReleaseIntArrayElements(i_,i,0); //遍历String数组 int32_t strLength = env->GetArrayLength(j); for (int k = 0; k < strLength; ++k) { jstring str = static_cast(env->GetObjectArrayElement(j, k)); const char* s = env->GetStringUTFChars(str,0); LOGE("获取java的String:%s",s); env->ReleaseStringUTFChars(str,s); } }

  • 获取Java中的自定义Bean参数
native void test2(Bean bean);

extern "C" JNIEXPORT void JNICALL Java_com_example_myjnitest_MainActivity_test2(JNIEnv *env, jobject instance, jobject bean) { //1.反射获取java对应的对象 jclass beanCls = env->GetObjectClass(bean); //2.反射获取要调用的方法,第三个参数为签名 jmethodID getName = env->GetMethodID(beanCls,"getName","()Ljava/lang/String; "); //3.调用 jstring name = static_cast(env->CallObjectMethod(bean, getName)); const char* nameStr = env->GetStringUTFChars(name,NULL); LOGE("获取到Bean的name:%s" ,nameStr); env->ReleaseStringUTFChars(name,nameStr); //获取setAge方法修改age参数,调用getAge方法获取age的值 jmethodID setAge = env->GetMethodID(beanCls,"setAge","(I)V"); env->CallVoidMethod(bean,setAge,88); jmethodID getAge = env->GetMethodID(beanCls,"getAge","()I"); int32_t age = env->CallIntMethod(bean,getAge); LOGE("修改Bean的age:%d",age); }

参数签名对照表:
Java类型 签名
boolean Z
short S
float F
byte B
int I
double D
char C
long J
void V
引用类型 L + 全限定名 + ;
数组 [+类型签名
C++中调用Java类的方法
extern "C" JNIEXPORT void JNICALL Java_com_example_myjnitest_MainActivity_test3(JNIEnv *env, jobject instance) { //构造方法创建对象 jclass beanCls = env->FindClass("com/example/myjnitest/Bean"); jmethodID constuct = env->GetMethodID(beanCls,"","()V"); jobject bean = env->NewObject(beanCls,constuct); //修改属性值 jfieldID nameId = env->GetFieldID(beanCls,"name","Ljava/lang/String; "); jstring nameStr = env->NewStringUTF("张学友"); env->SetObjectField(bean,nameId,nameStr); jfieldID ageId = env->GetFieldID(beanCls,"age","I"); env->SetIntField(bean,ageId,666); //调用Bean中的方法 jmethodID print = env->GetMethodID(beanCls,"print","()V"); env->CallVoidMethod(bean,print); }

JNI_OnLoad
调用System.loadLibrary()方法时,内部会查找so中的JNI_OnLoad方法,存在则调用。
动态注册
之前Java方法与C++方法匹配的方式,叫做静态注册。
另外,还可以使用动态注册,自定义方法名。
native void test4(int i); native String test5(String s);

//对应Java的test4方法 void test4444(JNIEnv *env, jobject instance, jint i) { LOGE("调用test4:%d", i); }//对应Java的test5方法 jstring test55555(JNIEnv *env, jobject instance, jstring s) { return env->NewStringUTF("返回test5"); }//需要动态注册的方法数组 static const JNINativeMethod mMethods[] = { {"test4", "(I)V",(void *) test4444}, {"test5", "(Ljava/lang/String; )Ljava/lang/String; ", (jstring *) test55555} }; //需要动态注册native方法的类名 static const char *mClassName = "com/example/myjnitest/MainActivity"; jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; //获取JNIEnv int r = vm->GetEnv((void **) &env, JNI_VERSION_1_4); if (r != JNI_OK) { return -1; } jclass mainActivityCls = env->FindClass(mClassName); r = env->RegisterNatives(mainActivityCls, mMethods, sizeof(mMethods) / sizeof(JNINativeMethod)); if (r != JNI_OK) { return -1; } return JNI_VERSION_1_4; }

native线程
native调用java需要使用JNIEnv这个结构体,而JNIEnv是由Jvm传入与线程相关的变量。可以通过JavaVM的AttachCurrentThread方法来获取到当前线程中的JNIEnv指针。
c++多线程调用MainActivity更新UI方法:
native void testThread(); public void updateUI() { if (Looper.myLooper() == Looper.getMainLooper()) { Toast.makeText(this, "C++更新UI", Toast.LENGTH_SHORT).show(); }else { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "C++更新UI", Toast.LENGTH_SHORT).show(); } }); } }

struct Context { jobject activity; }; JavaVM *_vm; //JNI_OnLoad中赋值void* threadTask(void *args) { //native线程 附加到 Java虚拟机 JNIEnv *env; jint i = _vm->AttachCurrentThread(&env,0); if(i != JNI_OK){ return 0; } //获取传入的MainActivity对象,调用MainActiviy中的updateUI方法 Context *context = static_cast(args); jclass cls = env->GetObjectClass(context->activity); jmethodID updateUI = env->GetMethodID(cls,"updateUI","()V"); env->CallVoidMethod(context->activity,updateUI); env->DeleteGlobalRef(context->activity); //释放资源 delete(context); context = 0; //分离 _vm->DetachCurrentThread(); return 0; }//动态注册 void testThread(JNIEnv *env,jobject instance) { pthread_t pid; Context *context = new Context; context->activity = env->NewGlobalRef(instance); pthread_create(&pid,0,threadTask,context); }

    推荐阅读