提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
JNI调用Java方法的流程是先通过类名找到类,然后再根据方法名找到方法的id,最后就可以调用这个方法了。如果是调用Java中的非静态方法,那么需要构造出类的对象后才能调用它。下面的例子演示了如何在JNI中调用Java的静态方法,以及非静态方法。
操作流程
新建JniTest类,代码如下
public class JniTest {
static {
System.loadLibrary("jni-test"); //加载libjni-test.so库
}
public native String callJniMethod(); //声明本地方法,调用so库代码
public static void staticMethodCalledByJni(String msgFromJni) {
//静态方法
Log.d("test","staticMethodCalledByJni,msg:" + msgFromJni);
}
public String methodCalledByJni(String msgFromJni) {
//非静态方法
Log.d("test","methodCalledByJni,msg:" + msgFromJni);
return "methodCalledByJni!!!";
}
}
在JNI中实现callJniMethod方法,并调用上面定义的静态方法和非静态方法。在项目
app目录下新建jni文件夹,在jni文件夹中创建test.c文件,代码如下:
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <android/log.h>
void
callJavaStaticMethod(JNIEnv *env, jobject thiz) {
//1. 获取JniTest类的Class引用
jclass clazz = (*env)->FindClass(env,"com/habit/jnitest/JniTest");
if (clazz == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "test", "find class JniTest error!\n");
return;
}
//2.获取JniTest类的staticMethodCalledByJni方法的id
jmethodID id = (*env)->GetStaticMethodID(env,clazz, "staticMethodCalledByJni", "(Ljava/lang/String;)V");
if (id == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "test", "find method staticMethodCalledByJni error!\n");
}
jstring msg = (*env)->NewStringUTF(env,"msg send by callJavaStaticMethod in test.c.");
//3.调用java的staticMethodCalledByJni方法
(*env)->CallStaticVoidMethod(env,clazz, id, msg);
}
jstring
callJavaMethod(JNIEnv *env, jobject thiz) {
//1. 获取JniTest类的Class引用
jclass clazz = (*env)->FindClass(env,"com/habit/jnitest/JniTest");
if (clazz == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "test", "find class JniTest error!\n");
return "";
}
//2. 获取类的默认构造函数ID
jmethodID construct_id = (*env)->GetMethodID(env,clazz, "<init>", "()V");
if (construct_id == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "test", "find method construct_id error!\n");
}
//3.实例化这个对象
jobject jobj = (*env)->NewObject(env,clazz, construct_id);
if (jobj == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "test", "NewObject jobj error!\n");
}
//4.获取JniTest类的methodCalledByJni方法的id
jmethodID id = (*env)->GetMethodID(env,clazz, "methodCalledByJni", "(Ljava/lang/String;)Ljava/lang/String;");
if (id == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "test", "find method methodCalledByJni error!\n");
}
jstring msg = (*env)->NewStringUTF(env,"msg send by callJavaMethod in test.c.");
//5.调用java的methodCalledByJni方法
return (jstring)(*env)->CallObjectMethod(env,jobj, id, msg);
}
jstring
Java_com_habit_jnitest_JniTest_callJniMethod(JNIEnv *env, jobject thiz) {
//调用静态方法
callJavaStaticMethod(env,thiz);
//调用非静态方法
jstring str = callJavaMethod(env,thiz);
char* str2 = (char*) (*env)->GetStringUTFChars(env,str, 0); //string 转char*
__android_log_print(ANDROID_LOG_ERROR, "test", "%s\n",str2);
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
备注:
(1)FindClass方法的其中一个参数是“包名/类名”,要改成自己的包名和类名。
(2)jni中对应java的方法名是Java_包名_类名_方法名,下方代码中的Java_com_habit_jnitest_JniTest_callJniMethod要改成自己项目的包名。
在jni文件夹创建Android.mk文件,文件内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#加入这句可以使得so文件打印日志
LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog
#生成so库的名称,生成后会添加前缀lib
LOCAL_MODULE := jni-test
LOCAL_SRC_FILES := test.c
include $(BUILD_SHARED_LIBRARY)
生成so库,并将so库剪切到app\src\main\jniLibs文件夹中。
生成so库的操作步骤参考AndroidStudio生成so库
在MainActivity中调用so库的方法,代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
JniTest jniTest = new JniTest();
System.out.println(jniTest.callJniMethod());
}
}
运行程序,程序正常运行的日志如下: