一、环境配置
本文使用的相关工具版本如下
- AndroidStudio 4.0.2
- gradle 6.1.1
NDK 是什么
NDK全称:Native Development Kit
原生开发套件 (NDK) 是一套工具,使您能够在 Android 应用中使用 C 和 C++ 代码,并提供众多平台库,您可使用这些平台库管理原生 activity 和访问实体设备组件,例如传感器和触控输入。NDK 可能不适合大多数 Android 编程初学者,这些初学者只需使用 Java 代码和框架 API 开发应用。然而,如果您需要实现以下一个或多个目标,那么 NDK 就能派上用场:
- 进一步提升设备性能,以降低延迟或运行游戏或物理模拟等计算密集型应用。
- 重复使用您自己或其他开发者的 C 或 C++ 库。
接下来然我们搭建一下NDK的开发环境
我这里选择的是我项目中使用的版本。当然推荐大家选择较新的版本。
环境配置非常简单,选择NDK和Cmake相关的安装包就可以了
创建NDK项目工程
- 和平时一样New Project
- 滑动到最下面选择 Native C++工程
- 这里有一个地方需要注意,包名不能带下划线
- 这里是选择C++的版本,这里我平时选择的是C++14,当然默认也可以。
- 接下来点击 finish ,等待创建好即可
- 项目创建完毕是一个现在的状态
- 下面我们点击运行即可
- 可以看到项目已经顺利跑起来了
这里注意一下:
- 我们创建完项目,顺利运行起来之后,这里是没有配置ndk路径的,这里我们配置一下ndk的路径,在平时开发中可以避免不必要的麻烦
项目结构
我们看一下现在的项目结构和平时我们创建的普通的有什么不同
- 我们可以看到 和普通的项目结构相比 现在的项目在main文件夹下多了一个cpp的文件目录
- 这个cpp文件目录中主要是存放我们编写的c++文件
- 现在这个cpp文件目录中有两个文件 native-lib.cpp 和 CMakeLists.txt
- native-lib.cpp:这是我们编写的c++代码文件,比如上面效果图中的 Hello from C++就是在该文件中创建的
- CMakeLists.txt:是cmake用来生成Makefile文件需要的一个描述编译链接的规则文件,包含了相关的配置信息,比如 指定cmake的版本
这张图中有
System.loadLibrary()
和 external
修饰的函数和平时项目中的有所不同
- System.loadLibrary():这个方法是加载so库使用的
- external:被该关键词修饰的方法,是本地方法,相当于Java中的 native,在Kotlin中声明方法,需要在C/C++中实现该方法
上面这张图中我们有两部分平时我们没有见过
- 第一部分
externalNativeBuild {
cmake {
cppFlags "-std=c++14"
}
}
该部分是我们在创建项目时,选择的C++版本
- 第二部分
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
path:指的是CMakelists.txt文件的路径
version:指的是Cmake的版本
- extern "C" :是指采用C的编译方式
- JNIEXPORT :JNI的一个关键字, 标记为该方法可以被外部调用
- JNICALL : JNI的一个关键字
- JNIEnv :C和Java相互调用的桥梁
- jobject: Java传递下来的对象
- std :命名空间
- hello.c_str() :将字符串转为char类型数组
- env ->NewStringUTF():创建jstring字符串对象
二、JNI互调
什么是JNI
JNI 的全称是 Java Native Interface,从名称上面翻译,它是一个 Java 和 C 语言的接口,通过这个翻译我们基本可以判定,这个 JNI 其实就是 Java 语言和 C 语言之间通讯的桥梁。
为什么要有JNI
因为 Java 和 C 之间无法直接通讯,无法直接通过代码显式调用,这中间需要一个翻译官来做这件事,而 JNI 出现的目的就是为了解决 Java 和 C 这两个不同语言之间的通讯问题。
下面从以下几个事例来介绍JNI互调
- C/C++ 调用 Java 方法
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void callPlusMethod();
public int onPlus(int number1, int number2) {
int num = number1 + number2;
return num;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
callPlusMethod();
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_ndk_dome_MainActivity_callPlusMethod(JNIEnv *env, jobject jobj) {
// 通过jobject获取到jclass
jclass j_clzz = env->GetObjectClass(jobj);
//通过jclass对象获取jmethodID
jmethodID methodID = env->GetMethodID(j_clzz, "onPlus", "(II)I");
//调用onPlus方法
env->CallIntMethod(jobj, methodID, 3, 2);
}
- C/C++ 修改 Java 变量
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public String name = "Delusion";
public native void changeName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
changeName();
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_ndk_dome_MainActivity_changeName(JNIEnv *env, jobject jobj) {
//获取属性所在class对象
jclass j_clz = env->GetObjectClass(jobj);
//获取属性jfieldID
jfieldID j_fid = env->GetFieldID(j_clz, "name", "Ljava/lang/String;");
//创建新的jstring对象
jstring jniName = env->NewStringUTF("Dian");
//修改为 Dian
env->SetObjectField(jobj, j_fid, jniName);
}
- C/C++ 创建Java对象
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native Point createPoint();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Point point = createPoint();
Log.e("---->", "Point: x--->"+point.x+" y--->"+point.y);
}
}
public class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public void setY(int y) {
this.y = y;
}
}
JNIEXPORT jobject JNICALL
Java_com_jni_dome_MainActivity_createPoint(JNIEnv *env, jobject jobj) {
//找到Point类
jclass j_clzz = env->FindClass("com/jni/dome/Point");
//获取构造方法的jmethodID
jmethodID j_mid = env->GetMethodID(j_clzz, "<init>", "(II)V");
//创建Point对象
jobject point = env->NewObject(j_clzz, j_mid, 23, 34);
//获取Point对象中x的jfieldID
jfieldID j_fid = env->GetFieldID(j_clzz, "x", "I");
//修改x的值
env->SetIntField(point, j_fid, 18);
//获取方法的jmethodID
jmethodID j_set_y_mid = env->GetMethodID(j_clzz, "setY", "(I)V");
//调用setY方法修改y的值
env->CallVoidMethod(point, j_set_y_mid, 18);
return point;
}
- Java 和 C/C++数据类型转换
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native String changeString(String str);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String string = changeString("Delusion");
Log.e("---->",string);
}
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_ndk_dome_MainActivity_changeString(JNIEnv *env, jobject thiz, jstring str) {
//将jstring转为char类型数组
const char *c_str = env->GetStringUTFChars(str, 0);
c_str = "new string";
//将char类型数组转为jstring类型
return env->NewStringUTF(c_str);
}
- C/C++ 中打印Android log
__android_log_print(ANDROID_LOG_ERROR,"TAG","-----> Log <-----");
日志等级
ANDROID_LOG_VERBOSE
ANDROID_LOG_DEBUG
ANDROID_LOG_INFO
ANDROID_LOG_WARN
ANDROID_LOG_ERROR
以上就是Java和C/C++相互调用的基础案例;