Неполадка с кодом JNI для Android в коде C ++: проблема GetPrimitiveArrayCritical () - PullRequest
1 голос
/ 19 сентября 2011

Я хочу написать собственный метод, который мог бы получать данные из сокета, а затем записывать обратно в ByteArray, который является входным параметром из Java. Поскольку я выделяю ByteArray на стороне Java и передаю его нативному методу, я знаю, что должен использовать GetPrimitiveArrayCritical (), чтобы получить указатель на этот ByteArray.

Однако мне все еще нужна эта процедура для запуска в потоке, поэтому я использую переменную jbyteArray структуры с именем socket_loop_native_data для запоминания этого входного ByteArray и использую pthread_create () для запуска нового потока с этой структурой в качестве входного параметра.

Наконец, в основном коде потока я использую GetPrimitiveArrayCritical (), чтобы получить указатель на предыдущий ByteArray, но это не удалось ...

Журнал ошибок:

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)

Часть моего нативного кода - что-то вроде

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 = 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);

    ...
}

ВМ, прерывающая выполнение инструкции

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

Итак ... Предположим, это означает, что я мог / не должен использовать переменную jbyteArray для запоминания входных данных ByteArray и вызова GetPrimitiveArrayCritical () в другом месте ???

Или я что-то забыл сделать ???

Любое предложение будет с благодарностью !!!

1 Ответ

2 голосов
/ 19 сентября 2011

Кажется, вы забыли "NewGlobalRef" указатель на байтовый массив nat->javaBuffer в вашем startSocketLoopNative.

Это должно быть причиной ваших неприятностей, так как вы не в том же потоке (новый env создается AttachCurrentThread, в котором, по моему опыту, все предыдущие неглобальные значения недействительны)


Примечание: Никогда не разрабатывался под JNI / Android, поэтому ваш код кажется странным для парня, который всегда кодировал J2SE / JNI:)

...