Android NativeActivity中JNI接收WebMessagePort消息的实现疑问
JNI层接收WebMessagePort消息的可行方案
方案一:Java层转发 + Unix管道通知C层
这是最贴合需求的方案,利用Unix管道在Java和C层间传递消息,结合ALooper_addFd让C层监听管道事件,实现异步通知。
步骤:
- JNI层创建管道:在C代码中创建匿名管道,将写端传递给Java层,读端添加到
ALooper监听。 - Java层处理Web消息:实现
WebMessageCallback,收到消息后序列化并写入管道。 - C层处理管道事件:
ALooper触发回调时读取管道数据,解析并处理业务逻辑。
代码示例:
Java端代码
import android.webkit.WebMessage; import android.webkit.WebMessagePort; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; public class MainActivity extends NativeActivity { private FileDescriptor[] pipe; // 初始化管道的JNI方法 public native void initPipe(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initPipe(); // 假设已获取WebMessagePort实例 WebMessagePort port = ...; port.setWebMessageCallback(0, new MyWebMessageCallback()); } private class MyWebMessageCallback extends WebMessagePort.WebMessageCallback { @Override public void onMessage(WebMessagePort port, WebMessage message) { // 序列化消息(示例用字符串,可按需用JSON/ProtoBuf) String msgContent = message.getData(); try { // 写入管道,用换行符做分隔符避免粘包 FileOutputStream os = new FileOutputStream(pipe[1]); os.write(msgContent.getBytes()); os.write('\n'); os.flush(); } catch (IOException e) { e.printStackTrace(); } } } }
JNI层C代码
#include <unistd.h> #include <fcntl.h> #include <string.h> #include <android/log.h> #include <android/looper.h> #include <jni.h> #define LOG_TAG "JNI_WEB_MSG" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) static int pipe_fd[2]; static ALooper* g_looper; // 管道事件回调 static int pipe_event_handler(int fd, int events, void* data) { if (events & ALOOPER_EVENT_INPUT) { char buffer[2048]; ssize_t read_size = read(fd, buffer, sizeof(buffer) - 1); if (read_size > 0) { buffer[read_size] = '\0'; // 处理消息(可解析序列化后的内容) LOGI("Received web message: %s", buffer); } return 1; // 保持监听 } return 0; // 停止监听 } JNIEXPORT void JNICALL Java_com_example_MainActivity_initPipe(JNIEnv* env, jobject thiz) { // 创建匿名管道 if (pipe(pipe_fd) == -1) { LOGE("Failed to create pipe"); return; } // 设置读端为非阻塞模式 fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK); // 获取当前线程的ALooper(NativeActivity的UI线程ALooper已关联Java Looper) g_looper = ALooper_forThread(); if (g_looper == NULL) { g_looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); } // 将管道读端添加到ALooper监听 ALooper_addFd(g_looper, pipe_fd[0], 0, ALOOPER_EVENT_INPUT, pipe_event_handler, NULL); // 将管道写端传递给Java层 jclass fd_cls = env->FindClass("java/io/FileDescriptor"); jmethodID fd_ctor = env->GetMethodID(fd_cls, "<init>", "()V"); jobject write_fd_obj = env->NewObject(fd_cls, fd_ctor); env->SetIntField(write_fd_obj, env->GetFieldID(fd_cls, "descriptor", "I"), pipe_fd[1]); jobjectArray pipe_array = env->NewObjectArray(2, fd_cls, NULL); env->SetObjectArrayElement(pipe_array, 1, write_fd_obj); env->SetObjectField(thiz, env->GetFieldID(env->GetObjectClass(thiz), "pipe", "[Ljava/io/FileDescriptor;"), pipe_array); env->DeleteLocalRef(write_fd_obj); env->DeleteLocalRef(pipe_array); env->DeleteLocalRef(fd_cls); }
方案二:直接通过JNI方法同步通知C层
如果不需要异步队列,可在Java层收到Web消息后直接调用JNI方法传递数据。需注意线程安全:WebMessageCallback.onMessage运行在UI线程,若C层逻辑在其他线程,需用同步机制处理。
代码示例:
Java端代码
private class MyWebMessageCallback extends WebMessagePort.WebMessageCallback { @Override public void onMessage(WebMessagePort port, WebMessage message) { // 直接调用JNI方法传递消息 nativeHandleWebMessage(message.getData()); } private native void nativeHandleWebMessage(String message); }
JNI层C代码
#include <pthread.h> #include <string.h> #include <android/log.h> #define LOG_TAG "JNI_WEB_MSG" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) static pthread_mutex_t msg_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t msg_cond = PTHREAD_COND_INITIALIZER; static char* g_pending_msg = NULL; JNIEXPORT void JNICALL Java_com_example_MainActivity_nativeHandleWebMessage(JNIEnv* env, jobject thiz, jstring message) { const char* msg_str = env->GetStringUTFChars(message, NULL); if (msg_str == NULL) return; pthread_mutex_lock(&msg_mutex); // 释放旧消息,复制新消息 if (g_pending_msg != NULL) { free(g_pending_msg); } g_pending_msg = strdup(msg_str); pthread_cond_signal(&msg_cond); pthread_mutex_unlock(&msg_mutex); env->ReleaseStringUTFChars(message, msg_str); } // C层事件循环示例 void c_main_loop() { while (1) { pthread_mutex_lock(&msg_mutex); while (g_pending_msg == NULL) { pthread_cond_wait(&msg_cond, &msg_mutex); } char* msg = g_pending_msg; g_pending_msg = NULL; pthread_mutex_unlock(&msg_mutex); // 处理消息 LOGI("Received message from Java: %s", msg); free(msg); } }
关键说明
- ALooper与Java Looper的独立性:二者分属Native和Java runtime的事件循环系统,Java层Handler的消息不会直接触发ALooper的FD事件,必须通过跨层通信机制(如管道、JNI调用)搭桥。
- 线程安全:Web消息回调在UI线程执行,若C层逻辑在非UI线程,必须用互斥锁、条件变量或消息队列保证线程安全。
- 消息序列化:传递复杂消息时,建议用JSON或ProtoBuf序列化,避免数据格式混乱。
内容的提问来源于stack exchange,提问作者Charles Lohr
相关产品推荐
相关产品推荐

