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

Android NativeActivity中JNI接收WebMessagePort消息的实现疑问

JNI层接收WebMessagePort消息的可行方案

方案一:Java层转发 + Unix管道通知C层

这是最贴合需求的方案,利用Unix管道在Java和C层间传递消息,结合ALooper_addFd让C层监听管道事件,实现异步通知。

步骤:

  1. JNI层创建管道:在C代码中创建匿名管道,将写端传递给Java层,读端添加到ALooper监听。
  2. Java层处理Web消息:实现WebMessageCallback,收到消息后序列化并写入管道。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 00:11:11