JNI 是 Java Native Interface 的缩写,译为 Java 本地接口,当 Java 无法处理一些任务时,就需要 JNI 技术来解决,例如游戏、音视频、图像处理等。
使用 AS 就可以创建 Native C++ 工程,这里以 AS 为例子。
然后一路 next,就可以创建一个 Native C++ 的项目。
创建的项目中,可以看到以下模板代码:
//MainActivity.java
public class MainActivity extends AppCompatActivity {
//加载 native 库
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
//本地方法
public native String stringFromJNI();
}
//native-lib.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_test_testcc_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
在 Java 中有两种数据类型:
- 基本数据类型: boolean 、char、byte、int、short、long、float、double。
- 引用数据类型: String、Object[]、Class、Object 等。
Java 数据类型到 JNI 层需要转换为 JNI 层的数据类型。
Java | JNI | Signature |
---|---|---|
boolean | jboolean | B |
byte | jbyte | B |
char | jchar | C |
short | jshort | S |
int | jint | I |
long | jlong | J |
float | jfloat | F |
double | jdouble | double (有符号 64 位双精度型) |
void | void | V |
除了 void ,其他基本数据类型只要在加前缀 j 就可以。
Java | JNI | Signature |
---|---|---|
Object | jobject | L+classname+; |
Class | jclass | L/java/lang/Class; |
String | jstring | L/java/lang/String; |
Throwable | jthrowable | L/java/lang/Throwable; |
Object[] | jobjectArray | [L+classname+; |
byte[] | jbyteArray | [D |
char[] | jcharArray | [C |
double[] | jdoubleArray | [D |
float[] | jfloatArray | [F |
int[] | jintArray | [I |
short[] | jshortArray | [S |
long[] | jlongArray | [J |
boolean[] | jbooleanArray | [Z |
在 Java 中有方法重载,JNI 仅仅依靠方法名称无法正确找到方法的,于是,就有了将参数类型和返回值类型组合一起,作为方法签名。
可以使用 javap 命令生成签名。
javap -s -p D:/Android/TestNative.class
//(LJava/lang/string)LJava/lang/string
JNIEnv 作用:
- 调用 Java 方法
- 操作 Java(操作 Java 中的变量和对象)
JNIEnv 只在创建它的线程生效,不能跨线程传递。
通过 JavaVM(虚拟在 JNI 层的代表,一个进程只有一个 JavaVM) 的 AttachCurrentThread 可以获取这个线程的 JNIEnv,切记要使用 DetachCurrentThread 释放资源。
#if defined(__cplusplus)
//C++
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
//C
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif