Android-NDK

What

首先解释下JNI是什么,Java Native Interface,Java本地化接口。众所周知Java是跨平台的语言,依托于不同平台的JVM运行。当Java需要和原生平台进行沟通时,这个时候就需要用道JNI,通过C/C++代码来实现Java的代码,这样Java就可以和原生代码(C/C++)进行相互调用了。

NDK: Native Develop Kit,是Android提供原生开发工具包。是为了更加方便JNI的开发,同时NDK里面还包含和交叉编译等工具。

Why

  • Java毕竟依托于JVM,本质上jdk的核心代码中的核心实现也都是去通过JNI去调用系统的库。
  • 原生代码相对于Java来说执行效率更高。同时核心实现Java完成不了的也是需要JNI。
  • 原生代码进行编译生成的库,相对于Java的jar来说反编译难度更大,安全性更高。
  • 便于移植

How

一个简单的JNI步骤

定义文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.test;

public class JniTest {
static {
System.loadLibrary("jni-test");//加载库
}

public static void main(String[] args) {

}

//定义native方法
public native String get();
public native void set(String str);
}

javac编译

1
javac com/test/JniTest.java

javah生成头文件

1
javah com.test.JniTest

之后src下会生成com_test_JniTest.h文件,这里面就是我们即将写的C/C++代码的头文件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_JniTest */

#ifndef _Included_com_test_JniTest
#define _Included_com_test_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_test_JniTest
* Method: get
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_test_JniTest_get
(JNIEnv *, jobject);

/*
* Class: com_test_JniTest
* Method: set
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_test_JniTest_set
(JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

里面就是包含了引用jni库,方法声明

JNI结构

方法的命名形如:Java_com_test_JniTest_set以Java+包名+类名+方法名,中间使用下划线进行隔开。

数据类型

基础类型

基本上在Java的基础类型的基础上加上小写的j即可,比如:

1
2
3
int -> jint
double -> jdouble
特殊:void -> void

tips:这里只是将Java的数据类型映射到C++中,不可以和C++的类型进行混淆。

引用类型

普通对象

1
2
3
Object -> jobject
Class -> jclass
String -> jstring

数组

1
2
Object[] -> jobjectArray
int[] -> jintArray

基本上以此类推

签名

C++调用Java

之前第一个步骤是Java调用C++,当然反过来也是可以的。既然我们在native方法中是会传入关于JVM环境,以及对象等。我们可以通过反射进行类加载,来调用方法。

Java文件有个方法

1
2
3
public static void test(String str) {
System.out.println("hello from java: msg is " + str);
}

C++中

这里我们调用静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void call_java_method(JNIEnv *env, jobject thiz) 
{
//获取类
jclass clazz = env->FindClass("com/test/Test");
if (clazz == NULL)
{
return;
}
//根据签名和方法名获取jmethodID
jmethodID id = env->GetStaticMethodID(clazz, "test", "(Ljava/lang/String/;)V");
if(id == NULL)
{
return;
}
jstring string = env->NewStringUTF("msg from C++");
//调用方法
env->CallStaticVoidMethod(clazz, id, msg);
}

调用普通普通方法

1
2


JNI原理

JNI引用