Android JNI
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是c&c++)
参考http://xdprof.sourceforge.net/doxygen/jni_8h.html
jni.h头文件路径:
- *\android studio\jre\include
- *\android studio\sdk\ndk-bundle\sysroot\usr\include
- Java程序中的函数可以调用Native语言写的函数,Native一般指的是C/C++编写的函数。
- Native程序中的函数可以调用Java层的函数,也就是说在C/C++程序中可以调用Java的函数
JAVA只能调用被编译成库的C\C++代码,怎么加载so库?
Android提供了3个实用的函数用来加载JNI库,分别是System.loadLibrary(libname),Runtime.getRuntime().loadLibrary(libname),以及Runtime.getRuntime().load(libFilePath)。
用loadLibrary函数加载
用System.loadLibrary(libname)和Runtime.getRuntime().loadLibrary(libname)这两个函数加载so库,不需要指定so库的路径,Android会默认从系统的共享库目录里面去查找,Android的共享库目录就是vendor/lib和system/lib,如果在共享库路径里面找到指定名字的so库,就会立即加载这个so库,所以我们给so库起名的时候要尽量避免和Android共享库里面的so库同名。如果在共享库目录里面查找不到,就会在APP的安装目录里面查找APP的私有so库,如果查找到,会立即加载这个so库。
用load函数加载
Runtime.getRuntime().load(libFilePath)用这个函数加载so库,需要指定完整的so库路径,优点是加载速度快,并且不会加载错误的so库,缺点就是需要指定完整的so库路径,有时候并不方便,大家常用的加载so库的方式还是用loadLibrary函数来加载。
加载so库示例:
1 2 3 4 5 6 7 8 9 |
static { System.loadLibrary("native-lib"); //用这种方式加载so库和System.loadLibrary函数加载so库的效果是一样的 //Runtime.getRuntime().loadLibrary("native-lib"); //String soLibFilePath; //用这种方式加载so库需要指定完整的so库路径 //Runtime.getRuntime().load(soLibFilePath); } public native String stringFromJNI(); |
native方法,就是在JAVA方法的前面加上native,这种方法是专门给JAVA调用C\C++调用的
本地c/c++代码:
1 2 3 4 5 6 7 8 9 10 |
#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_godgou_dlibtoandroid_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } |
这里的extern “C”的作用是让C++支持调用C语言的方法,如果你不需要,可以去掉;
JNIEXPORT xxx JNICALL代表这是一个JNI方法,其中xxx是返回值类型,如果是空类型,这里就是void;
Java_代表这是一个Java方法;
com_godgou_dlibtoandroid_MainActivity_stringFromJNI这段是你方法所在的包名以及它的方法名,在Java中相当于:com.godgou.dlibtoandroid.MainActivity.stringFromJNI
C语言并没有String型,如果需要使用它,需要借助C++的string工具包,把它转换成字符指针(char*):*. c_str()
返回文本:return env->NewStringUTF(hello.c_str());
C/c++语言调用JAVA代码:
在日常开发中,我们经常会遇到需要把C语言的数据回传给JAVA的情况,分同步和异步调用两种
同步调用:
同一条线程里调用JAVA代码
JAVA:
1 2 3 |
public void javafunction(String msg) { tv.setText(msg); } |
c/c++:
1 2 3 4 5 6 7 8 9 10 |
extern "C" JNIEXPORT jint JNICALL Java_com_godgou_dlibtoandroid_MainActivity_intFromJNI(JNIEnv *env, jobject instance, jint a, jint b) { //获取JAVA类 jclass clazz = env->FindClass("com/eternity/jnilab/MainActivity"); //获取方法ID jmethodID methodID = env->GetMethodID(clazz, "javafunction", "(Ljava/lang/String;)V"); std::string msg = "i am cpp "; env->CallVoidMethod(instance, methodID, env->NewStringUTF(msg.c_str())); } |
FindClass的入参是包名+类名的路径;
GetMethodID的入参是JAVA类,方法名,方法签名;
方法签名包含两个部分:
·括号里的内容是回传的数据内容,(Ljava/lang/String;)代表需要回传一个String型数据;
·括号外面的V代表这是一个void方法;
最后通过env->CallVoidMethod/CallStaticVoidMethod等等与Java方法对应的JNI方法调用JAVA。
来看看GetMethodID第三个参数是怎么表示的:
