最终的工程结构如下:
这个工程结构与使用ndk-build构建的工程结构不一样。 用ndk-build构建的工程结构中的文件夹叫做jni
,而这里叫做cpp
, 并且还有一个CMake的配置文件CMakeLists.txt
, 而ndk-build构建实际上使用的是gmake, 配置文件是Android.mk
和Application.mk
。
注意:CMake是比gmake更高级的构建系统。CMake的作用是智能的生成各种构建系统的配置文件,当然包括gmake的配置文件Makefile。 也就是说,从分层结构上来说,CMake更靠近开发者。 我们可以类比网络的分层模型,假如CMake等于HTTP协议;那么gmake就是IP协议。
package com.fpliu.jnitest;
public class XX {
static {
System.loadLibrary("xx");
}
public native String getText();
}
这里面特别注意:加载动态库的方式,通常是在Java类的静态代码块中,因为类加载器在加载一个类的时候, 类的静态成员和静态代码块最先执行,然后才会调用构造方法进行构造实例,之后,那些native
方法才能正确执行。 当然,你也可以在其他地方加载,只是需要进行判断操作。
被native
修饰的方法就是要使用C/C++实现的方法,所以,它只是声明,没有方法体。
被native
修饰的方法也可以被其他修饰符所修饰,比如static
。
1、切换到app/src/main/java
目录:
cd ~/JNITest/app/src/main/java
2、使用javah命令生成头文件
javah -jni com.fpliu.jnitest.XX
这样就在app/src/main/java
目录中生成了头文件com.fpliu.jnitest.XX.h
,文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_fpliu_jnitest_XX */
#ifndef _Included_com_fpliu_jnitest_XX
#define _Included_com_fpliu_jnitest_XX
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_fpliu_jnitest_XX
* Method: getText
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_fpliu_jnitest_XX_getText
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
1、把com.fpliu.jnitest.XX.h
剪切到app/src/main/cpp
目录中:
mv java/com.fpliu.jnitest.XX.h cpp/
2、编写实现文件native-lib.cpp
,内容如下:
#include "com_fpliu_jnitest_XX.h"
JNIEXPORT jstring JNICALL Java_com_fpliu_jnitest_XX_getText (JNIEnv *env, jobject obj) {
return env->NewStringUTF("Hello NDK !");
}
这里使用C++了, 因为AndroidStudio已经自动帮助你选择了,我们就不自己折腾了,毕竟,C++语言比C更安全,而且开发效率更高! 但是,如果你追求极致体验,并且算法的功力足够好的话,C还是首选, 毕竟,它的执行效率是最好的。
使用CMake构建的工程,在build.gradle
中的配置也不一样。
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
直接运行工程,看效果吧!
AndroidStudio创建的工程,自动支持了JNI
开发, 它默认使用C++语言实现, 使用CMake构建,让gradle支持了基本的一些配置, 使得我们用AndroidStudio开发JNI
也是比较省事的, 这是主流方向。当然,你喜欢用命令行的方式,不用AndroidStudio帮你做这些事情也是可以的。