在某些情况下,java编程已经不能满足我们的需要,比如一个复杂的算法处理,这时候就需要用到jni(java native interface)技术;
jni 其实就是java和c/cpp之间进行通信的一个接口规范,java可以调用c/cpp里面的函数,同样,c/cpp也可以调用java类的方法;
jni开发工具ndk的安装:
在最新的ndk版本中,安装ndk很简单,只需要装ndk的路径配置到系统环境变量中即可;
在编译的时候,进入工程根目录;执行命令 ndk-build 即可完成编译;
下面就通过一个例子一步一步的来初步学习jni
一、HelloWorld
新建一个工程,你甚至不需要其它额外的设置,然后在工程中添加一个jni目录,然后就可以开始了;
1.新建一个java类HelloWorld.java
package com.jni;public class HelloWorld { static {
System.loadLibrary("helloworld");
} public native String helloworld();
}
在HelloWorld中,定义了一个方法helloworld(),只不过这个方法被申明成了native的,并没有具体的实现,具体功能我们在接下来的cpp文件中实现;
2.在jni目录下添加一个helloworld.cpp
#include <jni.h>#include <android/log.h>#include <string.h>#ifndef _Included_com_jni_HelloWorld // 1#define _Included_com_jni_HelloWorld#ifdef __cplusplus // 2extern "C" {#endif // 2JNIEXPORT jstring JNICALL Java_com_jni_HelloWorld_helloworld(JNIEnv *, jobject);
#ifdef __cplusplus // 3}#endif // 3#endif // 1JNIEXPORT jstring JNICALL Java_com_jni_HelloWorld_helloworld(JNIEnv * env,
jobject obj) { return env->NewStringUTF("helloworld");
}
从上面这个cpp文件中可以很明白的看出,它有一个方法,具体包括方法申明和方法实现两个部分;但是相信大家也都看出来了,方法的命令很怪异,怎么这么长的方法名?
我们在这里先思考一个问题,java类中的方法是如何调用c++中的方法的呢?要解决这个问题,就得先来看这个长长的方法名;
其实这是jni的一个规范之一,用于映射java方法和c/c++中的方法对应;
再来看在cpp中定义的函数名:Java_com_jni_HelloWorld_helloworld
其实不难看出,java文件与cpp文件中函数名的配对定义方式为Java + 包名 + java类名 + 方法/函数名,中间用_分隔;其中两个参数分别是:
-
env:当前该线程的内容,包含线程里面全部内容;
obj:当前类的实例,指.java文件的内容(在该例子中即是HelloWorld类);
这里的helloworld方法,其实就只是返回了一个单词"helloworld";
3.在jni目录下添加一个Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := helloworld
LOCAL_SRC_FILES := helloworld.cpp
include $(BUILD_SHARED_LIBRARY)
4.在命令行下进入工程目录执行 ndk-build 命令,然后运行程序,调用HelloWorld实例的helloworld方法就可以得到它的返回字符串了;
二、jni调用Java类的方法(1)
通过上面的helloworld练手之后,我们来看一下jni调用java类里面的方法的实现;
1.新建设一个MethodCall.java文件如下
public class MethodCall { final String tag = "MethodCall"; static {
System.loadLibrary("methodcall");
} public native String jniCallMethod1(); public native String jniCallMethod2(); public native String jniCallStaticMethod(); public void javaMethod1() {
Log.e(tag, "javaMethod1");
} public String javaMethod2() {
Log.e(tag, "javaMethod2"); return "javaMethod2";
} public static void javaStaticMethod(String input) {
Log.e("MethodCall", "" + input);
}
}
该类有6个方法,其中有3个是java类的方法,另外3个是native方法,3个native方法分别去调用3个java方法;
2.添加三个native方法具体实现 methodcall.cpp
#include <jni.h>#include <android/log.h>#include <string.h>#ifndef _Included_com_jni_MethodCall#define _Included_com_jni_MethodCall#ifdef __cplusplusextern "C" {#endifJNIEXPORT jstring JNICALL Java_com_jni_MethodCall_jniCallMethod1(JNIEnv *,
jobject);
JNIEXPORT jstring JNICALL Java_com_jni_MethodCall_jniCallMethod2(JNIEnv *,
jobject);
JNIEXPORT jstring JNICALL Java_com_jni_MethodCall_jniCallStaticMethod(JNIEnv *,
jobject);
#ifdef __cplusplus
}#endif#endifJNIEXPORT jstring JNICALL Java_com_jni_MethodCall_jniCallMethod1(JNIEnv * env,
jobject obj) {
jmethodID mid; // 方法标识id
jclass cls = env->GetObjectClass(obj); // 类的对象实例
mid = env->GetMethodID(cls, "javaMethod1", "()V");
env->CallVoidMethod(obj, mid); return env->NewStringUTF("jniCallMethod1");
}
JNIEXPORT jstring JNICALL Java_com_jni_MethodCall_jniCallMethod2(JNIEnv * env,
jobject obj) {
jmethodID mid; // 方法标识id
jclass cls = env->GetObjectClass(obj); // 类的对象实例
mid = env->GetMethodID(cls, "javaMethod2", "()Ljava/lang/String;");
jstring js = (jstring) env->CallObjectMethod(obj, mid); return js;
}
JNIEXPORT jstring JNICALL Java_com_jni_MethodCall_jniCallStaticMethod(
JNIEnv * env, jobject obj) {
jmethodID mid; // 方法标识id
jclass cls = env->GetObjectClass(obj); // 类的对象实例
mid = env->GetStaticMethodID(cls, "javaStaticMethod", "(Ljava/lang/String;)V");
jstring input = env->NewStringUTF("jniCallStaticMethod->>javaStaticMethod");
env->CallStaticVoidMethod(cls, mid, input); return env->NewStringUTF("jniCallStaticMethod");
}
该cpp文件中有3个方法(我这里把方法名都写得很明白直观,相信不需要注释都知道是调用的哪一个java方法)