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

如何仅在JNI/Android Native Activity中创建自定义类(如ValueCallback<String>)

在JNI/Native Activity中生成Java接口实例并覆写方法的通用方案

针对你需要创建ValueCallback<String>这类接口实例并覆写方法的场景,JNI里的核心解决方案是动态代理(Dynamic Proxy)——因为这类接口没有默认实现类,必须通过代理机制绑定本地C代码的逻辑。以下是通用实现步骤:


1. 编写本地回调逻辑

先定义对应Java接口方法的C函数,处理实际业务逻辑:

// 对应ValueCallback<String>的onReceiveValue方法
void onReceiveValueCallback(JNIEnv *env, jobject proxy, jobject value) {
    // 将Java字符串转为C字符串
    const char *str = (*env)->GetStringUTFChars(env, (jstring)value, NULL);
    // 这里添加你的业务处理代码
    // ...
    // 释放资源
    (*env)->ReleaseStringUTFChars(env, (jstring)value, str);
}

2. 构建方法映射表

把Java接口的方法信息和本地函数绑定,注意方法签名要准确(泛型会被擦除,所以参数是Object):

static JNINativeMethod valueCallbackMethods[] = {
    {"onReceiveValue", "(Ljava/lang/Object;)V", (void*)onReceiveValueCallback}
};

方法签名可以通过javap -s android.webkit.ValueCallback命令获取。

3. 实现InvocationHandler桥接类

动态代理需要依赖InvocationHandler来转发方法调用到本地代码,先写一个Java类(打包到你的APK中):

public class NativeInvocationHandler implements InvocationHandler {
    private long nativeMethodTablePtr;

    public NativeInvocationHandler(long ptr) {
        nativeMethodTablePtr = ptr;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return nativeInvoke(nativeMethodTablePtr, proxy, method, args);
    }

    private native Object nativeInvoke(long nativePtr, Object proxy, Method method, Object[] args);
}

然后在JNI中实现nativeInvoke函数,负责匹配方法并调用本地回调:

jobject nativeInvoke(JNIEnv *env, jobject thiz, jlong nativePtr, jobject proxy, jobject method, jobjectArray args) {
    JNINativeMethod *methodTable = (JNINativeMethod*)nativePtr;
    
    // 获取调用的方法名
    jclass methodCls = (*env)->FindClass(env, "java/lang/reflect/Method");
    jmethodID getNameMid = (*env)->GetMethodID(env, methodCls, "getName", "()Ljava/lang/String;");
    jstring methodNameStr = (jstring)(*env)->CallObjectMethod(env, method, getNameMid);
    const char *methodName = (*env)->GetStringUTFChars(env, methodNameStr, NULL);

    // 匹配方法并执行本地回调
    for (int i = 0; i < sizeof(valueCallbackMethods)/sizeof(JNINativeMethod); i++) {
        if (strcmp(methodTable[i].name, methodName) == 0) {
            void (*callback)(JNIEnv*, jobject, jobject) = methodTable[i].fnPtr;
            jobject arg = args ? (*env)->GetObjectArrayElement(env, args, 0) : NULL;
            callback(env, proxy, arg);
            if (arg) (*env)->DeleteLocalRef(env, arg);
            break;
        }
    }

    // 清理资源
    (*env)->ReleaseStringUTFChars(env, methodNameStr, methodName);
    (*env)->DeleteLocalRef(env, methodNameStr);
    (*env)->DeleteLocalRef(env, methodCls);
    return NULL; // onReceiveValue返回void,返回NULL即可
}

4. 生成动态代理实例

通过JNI调用Proxy.newProxyInstance创建接口实例:

jobject createValueCallback(JNIEnv *env) {
    // 1. 获取ValueCallback接口类
    jclass valueCallbackCls = (*env)->FindClass(env, "android/webkit/ValueCallback");
    if (!valueCallbackCls) return NULL;

    // 2. 获取Proxy类和newProxyInstance方法
    jclass proxyCls = (*env)->FindClass(env, "java/lang/reflect/Proxy");
    jmethodID newProxyMid = (*env)->GetStaticMethodID(env, proxyCls,
        "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;");
    if (!proxyCls || !newProxyMid) {
        (*env)->DeleteLocalRef(env, valueCallbackCls);
        return NULL;
    }

    // 3. 获取类加载器
    jobject classLoader = (*env)->CallObjectMethod(env, valueCallbackCls,
        (*env)->GetMethodID(env, valueCallbackCls, "getClassLoader", "()Ljava/lang/ClassLoader;"));
    if (!classLoader) {
        (*env)->DeleteLocalRef(env, valueCallbackCls);
        (*env)->DeleteLocalRef(env, proxyCls);
        return NULL;
    }

    // 4. 构造接口数组
    jobjectArray interfaces = (*env)->NewObjectArray(env, 1,
        (*env)->FindClass(env, "java/lang/Class"), valueCallbackCls);
    if (!interfaces) {
        // 清理引用
        (*env)->DeleteLocalRef(env, valueCallbackCls);
        (*env)->DeleteLocalRef(env, proxyCls);
        (*env)->DeleteLocalRef(env, classLoader);
        return NULL;
    }

    // 5. 创建InvocationHandler实例
    JNINativeMethod *methodTableCopy = malloc(sizeof(valueCallbackMethods));
    memcpy(methodTableCopy, valueCallbackMethods, sizeof(valueCallbackMethods));
    
    jclass handlerCls = (*env)->FindClass(env, "com/your/package/NativeInvocationHandler");
    jmethodID handlerCtorMid = (*env)->GetMethodID(env, handlerCls, "<init>", "(J)V");
    jobject handlerObj = (*env)->NewObject(env, handlerCls, handlerCtorMid, (jlong)methodTableCopy);
    
    if (!handlerObj) {
        free(methodTableCopy);
        // 清理其他引用
        (*env)->DeleteLocalRef(env, valueCallbackCls);
        (*env)->DeleteLocalRef(env, proxyCls);
        (*env)->DeleteLocalRef(env, classLoader);
        (*env)->DeleteLocalRef(env, interfaces);
        (*env)->DeleteLocalRef(env, handlerCls);
        return NULL;
    }

    // 6. 生成代理实例
    jobject proxyObj = (*env)->CallStaticObjectMethod(env, proxyCls, newProxyMid,
        classLoader, interfaces, handlerObj);

    // 清理所有本地引用
    (*env)->DeleteLocalRef(env, valueCallbackCls);
    (*env)->DeleteLocalRef(env, proxyCls);
    (*env)->DeleteLocalRef(env, classLoader);
    (*env)->DeleteLocalRef(env, interfaces);
    (*env)->DeleteLocalRef(env, handlerCls);
    (*env)->DeleteLocalRef(env, handlerObj);

    return proxyObj;
}

通用流程总结

  1. 定义与Java接口方法对应的本地C函数;
  2. 构建JNINativeMethod映射表,关联Java方法和本地函数;
  3. 实现InvocationHandler桥接类,将Java方法调用转发到本地代码;
  4. 通过Proxy.newProxyInstance生成接口的代理实例,完成绑定。

注意事项

  • 严格管理JNI本地引用,避免内存泄漏;
  • 方法签名必须完全匹配,泛型参数会被类型擦除,需按擦除后的类型处理;
  • 动态代理仅适用于Java接口,若要生成类实例(非接口),需预先编写Java类并通过JNI调用构造方法。

内容的提问来源于stack exchange,提问作者Charles Lohr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 04:43:08