编译Java得到 class 文件,然后通过javah命令导出 JNI 的头文件
具体的命令:
javac D:\DuerOS\Android_art\14_ndk\src\main\java\com\why\a14_ndk\JniTest.java //编译Java源文件得到class文件 该类不能import Android中的包
javah -classpath D:\DuerOS\Android_art\14_ndk\src\main\java -d D:\DuerOS\Android_art\14_ndk\src\main\java\com\why\a14_ndk -jni com.why.a14_ndk.JniTest //通过javah命令导出JNI的头文件
javah -classpath 包名文件夹路径 -d 头文件输出路径 -jni 包名.类名(不带.class)
-classpath <路径> 用于装入类的路径。注意是包名文件夹的路径,不是class的路径,如果没有包名的话就是class的路径。
-d <目录> 输出目录
-jni 生成 JNI样式的头文件(默认)
1. 创建一个Android项目,声明所需的native方法
public class MainActivity extends Activity {
private TextView mTextView;
static {
/**
* Android.mk中LOCAL_MODULE 指定的名字就是生成的so文件名字(只不过没有前缀lib-),在这里加载
*/
System.loadLibrary("jni-test");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.msg);
mTextView.setText(get());
startActivity(new Intent(this, SecondActivity.class));
set("hello world from Main");
}
/**
* native的java方法如果有返回值,那么c/c++中必须保持一致,
*
* 如果java中是返回值是String,而c/c++中是void,会报错
*/
private native String get();
private native String set(String str);
}
2. 实现 Android 项目中所声明的 native 方法
如果在类种import Android包,那么不能使用 javac 。可以定义一个Jni.java,在Activity中生成一个Jni对象,通过该对象调用native方法。(javac,javah方便写c/c++,因为模板和方法名给写好了,生成头文件.h)
在与java的同级目录下创建一个名为 jni 的目录(名字不要修改,如果修改需要在 build.gradle 中指定)。在该jni目录下创建3
个文件text.cpp、Android.mk 和 Application.mk。
Android.mk
#makefile文件 作用就是向编译系统描述我要编译的文件在什么位置,要生成的文件叫什么名字,是什么类型
#获取当前路径
LOCAL_PATH := ${call my-dir}
#清除上次编译的信息
include $(CLEAR_VARS)
#指定最终生成的文件名字
LOCAL_MODULE := jni-test
#要编译的C的代码的文件名
LOCAL_SRC_FILES := test.cpp
#要生成的是一个动态链接库
include $(BUILD_SHARED_LIBRARY)
Application.mk
#指定so库的CPU平台的类型,比如armeabi,这里是x86(模拟器)
APP_ABI := x86
cpp
//第二个参数jobject 就是调用当前native方法的java对象
//第一个参数JNIEnv* JNIEnv是结构体JNINativeInterface这个结构体的一级指针
// JNIEnv*是结构体JNINativeInterface这个结构体的二级指针
//
// JNINativeInterface 定义了大量的函数指针,在JNI开发中非常常用 比如:env->NewStringUTF("Hello from JNI !")
//如果包名中有"_",一定要用"_1"来代替
JNIEXPORT jstring JNICALL Java_com_why_a14_1ndk_MainActivity_get
(JNIEnv * env, jobject thiz){
printf("invoke get in c++\n");
return env->NewStringUTF("Hello from JNI !");
}
在build.gradle
的buildTypes
中加上一下代码,指定so库的位置
sourceSets {
main {
//指定jni的代码路径
jni.srcDirs = []
}
}
3. 在命令行中通过 ” ndk-build “编译产生so库
ndk-build 之后(进入到工程中ndk-build),NDK 会创建一个和jni同级目录的 libs,libs 下存放的就是so库所在文件夹。
在与java同级的目录下创建一个名为 jniLibs 的目录(名字不要修改,如果修改需要在 build.gradle 中指定),将生成的so库所在文件夹复制到 jniLibs 目录中通过AS编译即可。
JNI 的数据类型包括两种,基本类型和引用类型。
基本类型有jboolean、jchar、jint等
引用类型有:类、对象和数组
他们和java中的数据类型对应关系见P484、P485
类型签名
类的签名:”L+包名+类名+ ; ” 。比如:java.lang.String,签名为“Ljava/lang/String;”
基本类型:大写字母表示:boolean-Z、byte-B、char-C、short-S、int-I、long-J、float-F、double-D、void-V
数组签名:”[+类型签名”。比如:char[] 签名为”[C”,String[] 签名为”[Ljava/lang/String;”
多维数组:”n个’[‘+类型签名”。比如:int[][] 签名为”[[I”
方法签名:(参数类型签名)+返回值类型签名。比如:boolean fun1(int a, double b, int[] b)
签名为”(ID[I)Z”
public class Jni {
private static final String TAG = "Jni";
public Jni(){}
public native String get();
public native void set(String string);
public static void methodCalledByJni(String msgFromJni){
Log.i(TAG, "methodCalledByJni: " + msgFromJni);
}
public String methodCallNotStaticByJni(String msgFromJni){
Log.i(TAG, "methodCallNotStaticByJni: " + msgFromJni);
return null;
}
}
jni.cpp
#include <jni.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
void callJavaStaticMethod(JNIEnv * env, jobject thiz){
//首先根据类名找到类
jclass clazz = env->FindClass("com/why/a14_ndk3/Jni");
if(clazz == NULL){
printf("find class MainActivity error");
return;
}
//void methodCalledByJni(String msgFromJni)
//再根据方法名找到方法id "(Ljava/lang/String;)V" 方法签名
jmethodID id = env->GetStaticMethodID(clazz, "methodCalledByJni", "(Ljava/lang/String;)V");
if(id == NULL){
printf("find method methodCalledByJni error");
}
jstring msg = env->NewStringUTF("msg send by callJavaStaticMethod in jni.cpp");
//调用java中的方法
env->CallStaticVoidMethod(clazz, id, msg);
}
void callJavaNotStaticMethod(JNIEnv * env, jobject thiz){
//首先根据类名找到类
jclass clazz = env->FindClass("com/why/a14_ndk3/Jni");
//String methodCallNotStaticByJni(String msgFromJni)
//再根据方法名找到方法id
//先找到构造方法的方法id,利用该id创建对象
jmethodID constructorId = env->GetMethodID(clazz, "" , "()V");
//根据类名和方法id创建类对象
jobject obj = env->NewObject(clazz, constructorId);
//找到要调用的方法的id
jmethodID id = env->GetMethodID(clazz, "methodCallNotStaticByJni", "(Ljava/lang/String;)Ljava/lang/String;");
//调用java中的方法
//env->CallVoidMethod(obj, id, env->NewStringUTF("msg send by callJavaNotStaticMethod in jni.cpp"));
env->CallObjectMethod(obj, id, env->NewStringUTF("msg send by callJavaNotStaticMethod in jni.cpp"));
}
JNIEXPORT jstring JNICALL Java_com_why_a14_1ndk3_Jni_get
(JNIEnv * env, jobject object){
return env->NewStringUTF("调用jni中的C");
}
JNIEXPORT void JNICALL Java_com_why_a14_1ndk3_Jni_set
(JNIEnv * env, jobject object, jstring string){
printf("设置数据成功\n");
//callJavaStaticMethod(env, object);
callJavaNotStaticMethod(env, object);
}
#ifdef __cplusplus
}
#endif
GetMethodID、CallStaticVoidMethod
等函数都是在jni.h中,可以在该文件夹中查看\android-ndk-r10e\platforms\android-21\arch-x86_64\usr\include