一般使用JNI都是调用C/C++代码的函数,但是很多时候在JNI中调用Java代码也是家常便饭。然后JNI中的C和C++的用法又略有不同,也在本博文中进行一次片段性的说一下。
目录结构就微微分为:
Java代码的实现
C代码的实现
CPP代码的实现
本案例中C和CPP代码的语法区别
Java代码的实现
其中jni.h是我拷贝进来做对比的。
MainActivity.java
public class MainActivity extends Activity {
private Button button1;
private TextView text1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1 = (Button) findViewById(R.id.button1);
text1 = (TextView) findViewById(R.id.text1);
text1.setText("");
final Jni jni = new Jni();
jni.setJniCall(new onJniCall() {
@Override
public void onListenerJniCall(String str) {
text1.append("#JniCall=" + str + "\n");
}
});
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
jni.call();
}
});
}
}
Jni.java
public class Jni {
private onJniCall jniCall;
public native void call();
public void setJniCall(onJniCall jniCall) {
this.jniCall = jniCall;
}
public Jni() {
System.loadLibrary("hello-jni");
}
private void onNative(String str) {
if (jniCall != null) {
jniCall.onListenerJniCall(str);
}
}
}
public interface onJniCall {
public void onListenerJniCall(String str);
}
AndroidManifest.xml没有进行修改
生成头文件
com_jni_Jni.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jni_Jni */
#ifndef _Included_com_jni_Jni
#define _Included_com_jni_Jni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jni_Jni
* Method: call
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_jni_Jni_call
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
C代码的实现
com_jni_Jni.c
#include <jni.h>
JNIEXPORT void JNICALL Java_com_jni_Jni_call
(JNIEnv * env, jobject obj) {
jclass ClassCJM = (*env)->FindClass(env, "com/jni/Jni");
jmethodID MethodDisplayMessage = (*env)->GetMethodID(env, ClassCJM, "onNative", "(Ljava/lang/String;)V");
jstring value = (*env)->NewStringUTF(env, "Hello World!-I am Native Jni (C-Language)");
(*env)->CallVoidMethod(env, obj, MethodDisplayMessage, value);
}
CPP代码的实现
com_jni_Jni.cpp
#include <jni.h>
extern "C" { //或者使用#include"com_jni_Jni_call.h"代替extern"C"
JNIEXPORT void JNICALL Java_com_jni_Jni_call
(JNIEnv * env, jobject obj) {
//jclass ClassCJM = env->GetObjectClass(obj);//效果一样
jclass ClassCJM = env->FindClass("com/jni/Jni");
jmethodID MethodDisplayMessage = env->GetMethodID(ClassCJM, "onNative", "(Ljava/lang/String;)V");
jstring value = env->NewStringUTF("Hello World!-I am Native Jni(CPP)");
env->CallVoidMethod(obj, MethodDisplayMessage, value);
}
}
关于这里的extern“C”的问题,引入头文件是因为头文件用了。不引入头文件的话,就手动在CPP代码中实现。
百度了一段关于extern "C"的说法extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。
加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。 这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。
这个功能主要用在下面的情况:1、C++代码调用C语言代码2、在C++的头文件中使用3、在多个人协同开发时,可能有的人比较擅长C语言,而有的人擅长C++
如果没有用到extern "C",就会报错:
运行效果:
使用CPP文件进行的。
本案例中C和CPP代码的语法区别
C语言 :(*env)-> FindClass(env,"java/lang/ClassLoader");
CPP : env->FindClass("java/lang/ClassLoader");
就这个简单的例子可以看出在CPP省略了一个参数,下面看一下原因:
在jni.h文件中看到:
#ifdef __cplusplus
jmethodID GetMethodID(jclass clazz, const char *name,
const char *sig) {
return functions->GetMethodID(this,clazz,name,sig);
}
这里就是调用了C语言中的functions的方法,然后加入自己的参数this,所以虽然都是调用同一个函数,但是在CPP中调用的时候可以省略一个参数,这个省略的参数已经由CPP在jni.h中代劳了。
下面看一下env的指针问题:
(*env)-> FindClass(env,"java/lang/ClassLoader");
env->FindClass("java/lang/ClassLoader");
可以看到原因是:
struct JNIEnv_;
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif
OK,自我理解一下就好。C++的知识丢了很多,所以以后再把解释补上。
题外话:今天周五,接下来迎来中秋3天小假期,公司发福利,心情愉悦。
本文来自CSDN博客,转载请联系作者注明出处http://blog.csdn.net/dreamintheworld