JNI民意调查问题

时间:2011-09-20 07:11:21

标签: android java-native-interface

我编写了一个本机方法,可以从socket接收数据,然后写回ByteArray,这是来自Java的输入参数。套接字是在BlueZ中创建的,并通过dBus消息传输到我的程序。我使用一个单独的线程来完成整个过程。

感谢Cerber对我以前的GetPrimitiveArrayCritical()问题的建议。现在,该程序可以正常运行。

然而,新问题是因为我使用Poll等待POLLIN事件,如果有可用于读取的传入数据,理论上会有POLLIN事件,我可以进行套接字读取。

不幸的是,POLLIN事件不断被触发,但我无法读取任何数据! 但是当我在BlueZ的代码中完成相同的程序时,这种奇怪的行为并没有发生。 我确定套接字是正确的。

我的本​​机代码就像:

struct socket_loop_native_data {
        pthread_mutex_t thread_mutex;
        pthread_t thread;
        struct pollfd *pollData;
        JavaVM *vm;
        int envVer;
        jobject me;
        jbyteArray javaBuffer;
        int bufferSize;
        jbyte *nativeBuffer;
        char *beginOfBuffer;
        char *endOfBuffer;
        int decodedDataSize;
        bool running;
};

typedef socket_loop_native_data native_data_t;

static jfieldID field_mNativeDataSocket;

static inline native_data_t *get_native_data(JNIEnv *env, jobject object) {
    return (native_data_t *)(env->GetIntField(object, field_mNativeDataSocket));
}

native_data_t *get_SocketLoop_native_data(JNIEnv *env, jobject object) {
    return get_native_data(env, object);
}

JNIEXPORT void JNICALL Java_android_classInitNativeSocket(JNIEnv* env, jclass clazz) {
    field_mNativeDataSocket = env->GetFieldID(clazz, "mNativeDataSocket", "I");
}

JNIEXPORT void JNICALL Java_android_initializeNativeDataNativeSocket(JNIEnv* env, jobject object) {

    native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
    if (NULL == nat) {
        LOGD("%s: out of memory!", __FUNCTION__);
        return;
    }
    memset(nat, 0, sizeof(native_data_t));

    pthread_mutex_init(&(nat->thread_mutex), NULL);

    env->SetIntField(object, field_mNativeDataSocket, (jint)nat);

}

JNIEXPORT jboolean JNICALL Java_android_startSocketLoopNative(JNIEnv *env, jobject object, jint sock, jbyteArray buffer, jint size) {

    jboolean result = JNI_FALSE;

    socket_loop_native_data *nat = get_native_data(env, object);

    pthread_mutex_lock(&(nat->thread_mutex));

    nat->running = false;

    if (nat->pollData) {
        LOGD("trying to start SocketLoop a second time!");
        pthread_mutex_unlock( &(nat->thread_mutex) );
        return JNI_FALSE;
    }

    nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd));
    if (!nat->pollData) {
        LOGD("out of memory error starting SocketLoop!");
        goto done;
    }

    memset(nat->pollData, 0, sizeof(struct pollfd));

    nat->pollData[0].fd = sock;
    nat->pollData[0].events = POLLIN;

    env->GetJavaVM( &(nat->vm) );
    nat->envVer = env->GetVersion();

    nat->me = env->NewGlobalRef(object);

    nat->javaBuffer = (jbyteArray)(env->NewGlobalRef(buffer));
    nat->bufferSize = (int)size;
    nat->decodedDataSize = 0;

    pthread_create(&(nat->thread), NULL, socketLoopMain, nat);
    result = JNI_TRUE;

done:
    if (JNI_FALSE == result) {
        if (nat->me) env->DeleteGlobalRef(nat->me);
        nat->me = NULL;
        if (nat->pollData) free(nat->pollData);
        nat->pollData = NULL;
    }

    pthread_mutex_unlock(&(nat->thread_mutex));

    return result;
}

static void *socketLoopMain(void *ptr) {

    native_data_t *nat = (native_data_t *)ptr;
    JNIEnv *env;

    JavaVMAttachArgs args;
    char name[] = "SocketLoop";
    args.version = nat->envVer;
    args.name = name;
    args.group = NULL;

    nat->vm->AttachCurrentThread(&env, &args);

    /* For poll result */
    int ret = 0;

    /* For receiving pollin data */
    int rlen;
    char *buffer = (char *)calloc(1, 65536);

    ...

    while ((nat->running)) {

        if ((ret = poll(nat->pollData, 1, -1)) < 0){
            LOGD("In socketLoopMain() : The socket poll error !!!");
            goto close;
        }

            if ((nat->pollData[0].revents & POLLIN)){

                    ...

                rlen = read(nat->pollData[0].fd, buffer, 65536);
                LOGD("In socketLoopMain() : Read bytes = %d", rlen);

                    ...
            }

            else if ((nat->pollData[0].revents & POLLOUT)){
                LOGD("In socketLoopMain() : The socket poll revents [POLLOUT] !!! DO NOTHING");
                continue;
            }
            else if ((nat->pollData[0].revents & POLLERR)){
                LOGD("In socketLoopMain() : The socket poll revents [POLLERR] !!!");
                goto close;
            }
            else if ((nat->pollData[0].revents & POLLHUP)){
                LOGD("In socketLoopMain() : The socket poll revents [POLLHUP] !!!");
                goto close;
            }
            else if ((nat->pollData[0].revents & POLLRDHUP) || (nat->pollData[0].revents & POLLNVAL)){
                LOGD("In socketLoopMain() : The socket poll revents [POLLRDHUP][POLLNVAL] !!!");
                goto close;
            }
    }
    ...
}

除第一个POLLIN外,我无法从套接字读取任何数据,rlen始终为0。

我使用Android源代码根目录中的命令“make libxxx”而不是“ndk-build”将整个本机代码构建到共享库。

任何建议都将非常感谢!!!

2 个答案:

答案 0 :(得分:0)

经过一些实验,我发现(我不确定这是否是正常和正确的行为......)

(1)在JNI中,当客户端本身关闭连接的套接字时,服务器不会意识到这一点。服务器部分将连续接收POLLIN事件但如果我们读取套接字,则读取的字节将为0 !!!

(2)在JNI中,当服务器轮询套接字超时并关闭连接的套接字时,客户端将不会意识到这一点。如果客户端尝试通过此关闭的套接字发送数据,则会收到返回值-1。

(3)在JNI中,如果我使用pthread_create并处理套接字,无论你是否将此线程附加到Java VM,套接字都无法正常工作!但是如果我用Java创建线程,而其他线程保持不变,它就可以工作!

这就是我发现的。也许,这是不正确的。请指出。 任何建议都将非常感谢!!!

答案 1 :(得分:0)

此页面将为您提供答案。 http://developer.android.com/training/articles/perf-jni.html

  1. 你不能在线程之间共享JNIEnv。
  2. 通过JNI连接的线程必须在退出之前调用DetachCurrentThread。