1.配置JNI环境
- 创建JNI文件夹
在项目的主目录中创建一个名为 JNI
的文件夹。这个文件夹将包含所有的本地源代码和配置文件。
- 编写Android.mk文件
这个文件是一个 Makefile,用来指导 NDK 如何编译和构建本地代码。
#清除之前定义的变量,确保每个模块的配置从干净的状态开始。
LOCAL_PATH:=$(call my-dir)
#清除之前定义的变量,确保每个模块的配置从干净的状态开始。
include $(CLEAR_VARS)
#定义了生成的动态链接库的名称为OBOJni,在Android系统中会自动添加前缀lib和后缀.so,成为libOBOJni.so
LOCAL_MODULE := OBOJni
#指定test.cpp为此动态库的源文件。
LOCAL_SRC_FILES := test.cpp
#指定链接器链接Android系统的日志库liblog,以便库中可以使用日志功能。
LOCAL_LDLIBS := -llog
#引入构建系统用来编译和链接生成动态链接库的规则和命令。
include $(BUILD_SHARED_LIBRARY)
- 配置build.gradle文件:
修改项目中的 app/build.gradle
文件,在 android{}
块中添加以下配置,以指定 JNI 库文件的存储路径和指向 Android.mk
的路径:
sourceSets {
main {
// 设置 JNI 库的路径
jniLibs.srcDirs = ['../libs']
}
}
externalNativeBuild {
ndkBuild {
//表示构建androidJNI的Android.mk的所在路径
path '../jni/Android.mk'
}
}
- 创建和编写test.cpp
#include <jni.h>
#include <android/log.h>
void hello_test_jni()
{
__android_log_print(ANDROID_LOG_ERROR,"JNI","hello JNI test!!!");
}
- 进入JNI路径,使用powershell编译
ndk-build.cmd
能生成 .so
文件,则表示 C++ 编译环境配置正确。
2.java调用JNI模块
- 创建JNI调用类
在 src
目录下,与 Activity 类同级,创建一个用于 JNI 调用的 Java 类 OBJNI.java
。这个类将封装所有 JNI 相关的操作:
public class OBJNI {
// 单例模式确保全局只有一个实例
private static OBJNI instance = null;
public static OBJNI getInstance() {
if (instance == null) {
instance = new OBJNI();
}
return instance;
}
// 声明 native 方法
public native void hello_jni();
// 加载 C++ 编写的库
static {
System.loadLibrary("testjni"); // 库名需与 Android.mk 中的 LOCAL_MODULE 相同
}
}
- 生成 native 方法的 C++ 声明:
使用 javah
工具从 OBOJNI
类生成对应的 C++ 头文件。打开命令行工具,导航至 Java 源文件所在的目录,并执行:
javah -classpath . -jni com.afison.ob.OBJNI
- 找到com_itcast_ace_obo_170325_OBOJNI.h中的对应函数声明 拷贝到 test.cpp中去实现
注意 一定要在extern C {} 中去实现 否则函数名会改变
- 重新编译cpp程序
cd C:\Users\Ace\Documents\GitHub\OBO\OBO_170325\JNI>
ndk-build.cmd
生成新的so
- 在java中 Activity中 调用此方法
OBOJNI.getInstance().hello_jni();
如果控制台能够输出cpp的日志 表示代用成功
3.java调用传参int类型的jni接口
- 编写native方法
- 将java函数生成C++接口
- C++接口
- 调用
Java代码中调用
- 重新编译
- 运行结果
4.java调用传参bool类型的jni接口
- 编写native方法
- 将java函数生成C++接口
- C++接口
test.cpp中
JNIEXPORT jboolean JNICALL Java_com_afison_ob_OBOJNI_jni_1test2
(JNIEnv *env, jobject obj, jboolean j_bool)
{
bool arg_bool = (j_bool == JNI_TRUE)?true: false;
__android_log_print(ANDROID_LOG_ERROR,"JNI","JNI:bool = %s",(arg_bool == true)?"true":"false");
return (arg_bool == true)?JNI_TRUE:JNI_FALSE;
}
- 调用
Java代码中调用
- 重新编译
- 运行结果
5.java调用传参String类型jni接口
- 编写native方法
- 将java函数生成C++接口
- C++接口
JNIEXPORT jstring JNICALL Java_com_afison_ob_OBOJNI_jni_1test3
(JNIEnv *env, jobject obj, jstring j_str1, jstring j_str2) {
if (j_str1 == NULL || j_str2 == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "JNI", "Input string is NULL");
return NULL; // Optionally throw a Java exception
}
const char *c_str1 = env->GetStringUTFChars(j_str1, 0);
const char *c_str2 = env->GetStringUTFChars(j_str2, 0);
if (c_str1 == NULL || c_str2 == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "JNI", "Failed to convert Java string to native C string");
return NULL; // Handle the error, maybe throw an exception
}
__android_log_print(ANDROID_LOG_ERROR, "JNI", "c_str1 = %s", c_str1);
__android_log_print(ANDROID_LOG_ERROR, "JNI", "c_str2 = %s", c_str2);
jstring ret_jString = env->NewStringUTF("JNI return string");
env->ReleaseStringUTFChars(j_str1, c_str1);
env->ReleaseStringUTFChars(j_str2, c_str2);
return ret_jString;
}
- 调用
Java代码中调用
bt_jni_4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String str1 = "Hello";
String str2 = "World!";
String str3 = OBOJNI.getInstance().jni_test3(str1,str2);
Log.e("JNI", "Java:String="+str3);
}
});
- 重新编译
- 运行结果
6.Bug解决
More than one file was found with OS independent path 'lib/arm64-v8a/libOBOJni.so'
配置packagingOptions:在你的app模块的build.gradle
文件中,使用packagingOptions
来解决冲突。你可以通过添加如下配置来告诉Gradle在打包时如何处理重复的so文件:
javah -classpath . -jni com.afison.ob.OBJNIandroid {
packagingOptions {
pickFirst 'lib/arm64-v8a/libOBOJni.so'
// 或者使用exclude排除特定的so文件,如:
// exclude 'lib/arm64-v8a/libOBOJni.so'
}
}
JNI调用成功