如何仅在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; }
通用流程总结
- 定义与Java接口方法对应的本地C函数;
- 构建
JNINativeMethod映射表,关联Java方法和本地函数; - 实现
InvocationHandler桥接类,将Java方法调用转发到本地代码; - 通过
Proxy.newProxyInstance生成接口的代理实例,完成绑定。
注意事项
- 严格管理JNI本地引用,避免内存泄漏;
- 方法签名必须完全匹配,泛型参数会被类型擦除,需按擦除后的类型处理;
- 动态代理仅适用于Java接口,若要生成类实例(非接口),需预先编写Java类并通过JNI调用构造方法。
内容的提问来源于stack exchange,提问作者Charles Lohr
相关产品推荐
相关产品推荐

