You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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命令生成:

  1. 先编译Java类得到.class文件
  2. 打开终端,进入.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:57:46