开发者

Android JNI native C++ code GetPrimitiveArrayCritical() problem

I want to write a native method that could receive data from socket and then write back to a ByteArray which is input parameter from Java. Because I allocate ByteArray in Java side, and pass it to native method, I know I should use GetPrimitiveArrayCritical() to get the pointer of this ByteArray.

However, I still need this procedure to run in a thread, so I use a jbyteArray variable of a struct called socket_loop_native_data to remember this input ByteArray, and use pthread_create() to start a new thread with this struct as input parameter.

Finally, in the main body code of the thread, I use GetPrimitiveArrayCritical() to get the pointer of the previous ByteArray, but it failed...

The error log are :

14:09:32.350 Warning dalvikvm 2780  JNI WARNING: 0x4077ab48 is not a valid JNI reference
14:09:32.350 Warning dalvikvm 2780               in Ldalvik/system/NativeStart;.run ()V (GetPrimitiveArrayCritical)

The piece of my native code are something like

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_initializeNati开发者_运维技巧veDataNativeSocket(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 = 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);

    ...

    nat->nativeBuffer = (jbyte *)(env->GetPrimitiveArrayCritical((nat->javaBuffer), NULL));     

    nat->beginOfBuffer = (char *)(&(nat->nativeBuffer[0]));
    nat->endOfBuffer = (char *)(&(nat->nativeBuffer[0])) + (nat->bufferSize);

    ...
}

The VM aborting after execute the statement

nat->nativeBuffer = (jbyte *)(env->GetPrimitiveArrayCritical((nat->javaBuffer), NULL));

So... Dose it mean that I could/should not use a jbyteArray variable to remember the input ByteArray and call GetPrimitiveArrayCritical() in another place ???

Or, is anything I forget to do ???

Any suggestion would be greatly appreciated !!!


It seems that you forgot to "NewGlobalRef" your pointer to the byte array nat->javaBuffer in your startSocketLoopNative.

It should be the cause of your troubles since you're not in the same Thread (new env is created by AttachCurrentThread in which, from my experience, all previous non-globals are invalid)


Side note: Never developped under JNI/Android so your code seems wierd for a guy that always coded J2SE/JNI :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜