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

Android JNI:Java继承C++库抽象类并实现回调的问题咨询

在Android中实现Java到C++的回调(JNI实战指南)

我来一步步帮你搞定这个JNI回调的问题,其实核心就是搭建Java和C++之间的桥接层,咱们分阶段拆解,每个环节都给你具体代码示例:

第一步:Java端先搞定回调接口和实现类

首先,Java没法直接继承C的抽象类,所以咱们先在Java里定义一个和C CallbackClass 对应的接口,然后写具体的实现类:

定义Java回调接口

这个接口要和C抽象类的方法一一对应,比如C有update方法,Java也定义同名同参数的方法:

// 对应C++的CallbackClass抽象类的Java接口
public interface CallbackInterface {
    // 参数类型可以根据你的C++方法调整,这里用String做示例
    void update(String callbackData);
}

写具体的回调实现类

接下来实现这个接口,把你要在回调里做的逻辑写进去,比如打印日志或者更新UI:

public class MyCallback implements CallbackInterface {
    @Override
    public void update(String callbackData) {
        // 这里写回调触发后的逻辑,比如打印数据
        System.out.println("收到C++的回调数据:" + callbackData);
        
        // 如果要更新Android UI,记得切到主线程!
        // 比如在Activity里可以这么写:
        // runOnUiThread(() -> {
        //     yourTextView.setText(callbackData);
        // });
    }
}

第二步:JNI层做Java和C++的桥接

这部分是核心,咱们需要写一个JNI包装类,它继承C的CallbackClass,同时持有Java回调对象的引用,负责把C的调用转发给Java。

先确认C++端的抽象类

假设你的C++库原本的抽象类是这样的:

#include <string>

// C++端的抽象回调类
class CallbackClass {
public:
    virtual ~CallbackClass() = default;
    // 抽象方法,等待子类实现
    virtual void update(const std::string& data) = 0;
};

编写JNI包装类

这个类是关键的桥接器,它要做的事情:保存Java对象的全局引用(避免局部引用被回收)、缓存Java方法的ID、重写C++的update方法来调用Java的方法:

#include <jni.h>
#include "CallbackClass.h"

// JNI回调包装类,继承C++的CallbackClass
class JNICallbackBridge : public CallbackClass {
private:
    JNIEnv* mEnv;
    jobject mJavaCallback; // Java回调对象的全局引用
    jmethodID mUpdateMethodId; // 缓存的update方法ID

public:
    // 构造函数:初始化全局引用和方法ID
    JNICallbackBridge(JNIEnv* env, jobject javaCallback) {
        mEnv = env;
        // 创建全局引用,因为JNI局部引用在方法返回后会被自动释放
        mJavaCallback = env->NewGlobalRef(javaCallback);

        // 获取Java回调接口的Class对象
        jclass callbackClazz = env->GetObjectClass(javaCallback);
        // 查找update方法的ID,方法签名必须和Java方法完全匹配
        // 这里的签名"(Ljava/lang/String;)V"表示:参数是String,返回值是void
        mUpdateMethodId = env->GetMethodID(callbackClazz, "update", "(Ljava/lang/String;)V");

        // 释放局部引用的Class对象,避免内存泄漏
        env->DeleteLocalRef(callbackClazz);
    }

    // 析构函数:释放全局引用,一定要做!
    ~JNICallbackBridge() {
        mEnv->DeleteGlobalRef(mJavaCallback);
    }

    // 重写C++的update方法,转发调用到Java端
    void update(const std::string& data) override {
        // 把C++的std::string转换成Java的String对象
        jstring javaData = mEnv->NewStringUTF(data.c_str());
        // 调用Java对象的update方法
        mEnv->CallVoidMethod(mJavaCallback, mUpdateMethodId, javaData);
        // 释放局部引用的String对象
        mEnv->DeleteLocalRef(javaData);

        // 可选:检查JNI调用是否抛出异常,避免崩溃
        if (mEnv->ExceptionCheck()) {
            mEnv->ExceptionClear();
            // 这里可以加异常处理逻辑
        }
    }
};

实现JNI注册方法

接下来写一个JNI导出方法,让Java可以调用它,把Java回调对象传递过来,然后创建桥接类实例注册到C++库:

// 假设你的C++库有一个注册回调的函数,比如:
void RegisterForSubscription(CallbackClass* callback);

// JNI导出方法,注意方法签名要对应Java中的Native方法
// 格式:Java_包名_类名_方法名,包名用下划线代替点
extern "C" JNIEXPORT void JNICALL
Java_com_your_app_package_NativeHelper_registerCallback(
        JNIEnv* env, jobject /* this */, jobject javaCallback) {
    // 创建JNI桥接类实例
    JNICallbackBridge* callbackBridge = new JNICallbackBridge(env, javaCallback);
    // 注册到C++库中
    RegisterForSubscription(callbackBridge);
}

第三步:Java端调用JNI注册回调

在Java中声明Native方法,加载JNI库,然后创建回调实例并注册:

public class NativeHelper {
    // 加载JNI库,库名是你编译出来的so库前缀(比如libmy-native.so,这里写my-native)
    static {
        System.loadLibrary("my-native");
    }

    // 声明Native注册方法,参数是咱们定义的CallbackInterface
    public native void registerCallback(CallbackInterface callback);

    // 使用示例:在合适的地方调用这个方法注册回调
    public void setupCallback() {
        MyCallback myCallback = new MyCallback();
        registerCallback(myCallback);
    }
}

第四步:C++端触发回调

当C++库需要通知Java时,直接调用CallbackClass的update方法就行,因为咱们的桥接类已经重写了这个方法,会自动转发到Java端:

// 假设C++库中有某个业务逻辑需要触发回调
void triggerCallback() {
    // 这里假设你已经保存了注册进来的CallbackClass指针
    CallbackClass* registeredCallback = getRegisteredCallback();
    if (registeredCallback != nullptr) {
        // 调用update方法,会自动触发Java端的回调
        registeredCallback->update("这是来自C++的回调消息!");
    }
}

几个必须注意的坑

  • 全局引用一定要释放:如果忘了在析构函数里调用DeleteGlobalRef,会导致Java对象无法被GC回收,造成内存泄漏。
  • 方法签名不能错:JNI查找方法时签名必须完全匹配,你可以用javap -s 你的类名命令生成正确的签名,比如对CallbackInterface执行这个命令,就能看到update方法的签名。
  • 线程问题:如果C++是在子线程调用回调,那Java的update方法会运行在同一个子线程,Android更新UI必须在主线程,所以记得用runOnUiThread或者Handler切换线程。
  • 异常处理:JNI调用Java方法可能会抛出异常,一定要检查并清除,否则会导致程序崩溃。

内容的提问来源于stack exchange,提问作者Chris R

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:41:42