Android项目引入.so文件及C++调用Java方法的实现步骤咨询
嘿,我来一步步帮你搞定把.so集成到Android项目,并且实现C++调用Java方法的需求!
第一步:确认.so文件的放置与项目配置
- 你已经把.so放到
jniLibs目录了,先检查目录结构是否正确:得是app/src/main/jniLibs/[ABI架构]/xxx.so的形式,比如armeabi-v7a、arm64-v8a这些文件夹对应不同的CPU架构,每个架构下放对应版本的.so。 - 新版Android Studio默认会自动识别
jniLibs目录,但如果是旧版本或者遇到加载问题,可以在app模块的build.gradle里手动指定:android { sourceSets { main { jniLibs.srcDirs = ['src/main/jniLibs'] } } }
第二步:在Java层定义交互的方法
先在Java类里做好准备:一方面声明native方法用来触发C逻辑,另一方面定义好要被C调用的Java方法:
public class NativeHelper { // 加载.so库,注意这里的库名要去掉前缀`lib`和后缀`.so`,比如`libtest.so`就写"test" static { System.loadLibrary("test"); } // 声明native方法,用来启动C++侧的逻辑 public native void startNativeLogic(); // 要被C++调用的Java方法,这里用public或包访问权限都可以,注意方法签名要和C++侧对应 public void onNativeCall(String message) { Log.d("NativeHelper", "收到C++传来的消息:" + message); } }
第三步:C++层实现调用Java方法的逻辑
这里分两种常见场景来实现:
场景1:在native方法内部直接调用Java方法
当你实现startNativeLogic这个native方法时,可以直接拿到JNIEnv和当前Java对象的引用,直接调用目标方法:
#include <jni.h> #include <string> extern "C" JNIEXPORT void JNICALL Java_com_example_yourpackage_NativeHelper_startNativeLogic(JNIEnv* env, jobject instance) { // 1. 找到对应的Java类:参数是类的全路径,用/代替. jclass cls = env->FindClass("com/example/yourpackage/NativeHelper"); if (cls == nullptr) { return; // 找不到类直接返回 } // 2. 找到要调用的方法:参数是方法名、方法签名(可以用javap工具生成) // 比如void onNativeCall(String)的签名是(Ljava/lang/String;)V jmethodID methodId = env->GetMethodID(cls, "onNativeCall", "(Ljava/lang/String;)V"); if (methodId == nullptr) { env->DeleteLocalRef(cls); return; } // 3. 创建Java字符串对象 jstring msg = env->NewStringUTF("Hello from C++!"); // 4. 调用Java方法,instance就是当前NativeHelper的对象实例 env->CallVoidMethod(instance, methodId, msg); // 5. 释放局部引用,避免内存泄漏 env->DeleteLocalRef(msg); env->DeleteLocalRef(cls); }
场景2:在C++的其他线程中调用Java方法
如果是在C++的独立线程里调用Java方法,需要先把当前线程附加到JavaVM上获取JNIEnv,还要保存Java对象的全局引用(避免被GC回收):
#include <jni.h> #include <string> #include <pthread.h> // 保存全局的JavaVM引用,在JNI加载时初始化 JavaVM* g_jvm = nullptr; // 保存全局的Java对象引用 jobject g_native_helper = nullptr; // JNI加载时的回调,用来获取JavaVM实例 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { g_jvm = vm; JNIEnv* env = nullptr; if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) { return -1; } return JNI_VERSION_1_6; } // C++线程函数示例 void* thread_func(void* arg) { JNIEnv* env = nullptr; // 把当前线程附加到JavaVM,获取JNIEnv int result = g_jvm->AttachCurrentThread(&env, nullptr); if (result != JNI_OK || env == nullptr) { return nullptr; } // 找到目标类和方法,步骤和之前一致 jclass cls = env->FindClass("com/example/yourpackage/NativeHelper"); if (cls == nullptr) { g_jvm->DetachCurrentThread(); return nullptr; } jmethodID methodId = env->GetMethodID(cls, "onNativeCall", "(Ljava/lang/String;)V"); if (methodId == nullptr) { env->DeleteLocalRef(cls); g_jvm->DetachCurrentThread(); return nullptr; } jstring msg = env->NewStringUTF("Hello from C++ thread!"); env->CallVoidMethod(g_native_helper, methodId, msg); // 释放资源并分离线程 env->DeleteLocalRef(msg); env->DeleteLocalRef(cls); g_jvm->DetachCurrentThread(); return nullptr; } // 在native方法里初始化全局对象并启动线程 extern "C" JNIEXPORT void JNICALL Java_com_example_yourpackage_NativeHelper_startNativeLogic(JNIEnv* env, jobject instance) { // 创建全局引用,保存Java对象实例 g_native_helper = env->NewGlobalRef(instance); pthread_t tid; pthread_create(&tid, nullptr, thread_func, nullptr); }
第四步:快速生成方法签名的小技巧
如果你不确定Java方法对应的JNI签名,可以用javap命令生成:
- 先编译Java类得到.class文件
- 打开终端,进入.class文件所在目录,执行:
输出里会显示每个方法的签名,比如:javap -s -p com.example.yourpackage.NativeHelper
这里的public void onNativeCall(java.lang.String); descriptor: (Ljava/lang/String;)V(Ljava/lang/String;)V就是你需要的方法签名。
关键注意事项
- 注意JNI引用的管理:局部引用(比如
FindClass、NewStringUTF返回的)要及时用DeleteLocalRef释放;全局引用(NewGlobalRef创建的)不再使用时要用DeleteGlobalRef释放,避免内存泄漏。 - 如果要调用的是Java静态方法,要用
GetStaticMethodID和CallStaticVoidMethod(对应返回值类型的方法)来调用。 - 确保.so的架构和测试设备的架构一致,比如只放了
arm64-v8a的.so,就不要在armeabi-v7a的设备上测试,否则会报错找不到库。
内容的提问来源于stack exchange,提问作者ishan jain
相关产品推荐
相关产品推荐

