【问题标题】:JNI getting error message after exiting conditinal loop when the method returns方法返回时退出条件循环后JNI收到错误消息
【发布时间】:2021-08-29 04:04:06
【问题描述】:

我是 c 新手,我正在尝试在 android studio 中创建这个项目,该项目使用 /dev/input/event* 目录读取 linux 输入,读取的另一部分是可执行程序(以 root 访问权限运行),写入一个可以用这个程序读取的fifo管道(命名)并将input_event结构发送回java。

C 代码:

boolean READ_EVENT_TASK_LOOP = false;
pthread_t eventThread, eventTest;

void Java_com_yacine_1app_bixby_1map_event_CustomEvent_releaseListener(JNIEnv *env, jobject obj){
    READ_EVENT_TASK_LOOP = false;

    jclass ceClass = (*env)->GetObjectClass(env, obj);

    jmethodID onEventId = (*env)->GetMethodID(env, ceClass, "onError", "(Ljava/lang/String;)V");

    char msg[] = "No error were found!";

    (*env)->CallVoidMethod(env, obj, onEventId, (*env)->NewStringUTF(env, msg));

}

void * eventCheckingThread(JNIEnv *env, jobject obj, jstring shared_path);

void Java_com_yacine_1app_bixby_1map_event_CustomEvent_listenTo(JNIEnv *env, jobject obj, jstring shared_path) {

    int stat = pthread_create(&eventThread, NULL, eventCheckingThread(env, obj, shared_path), "Event Thread");

    if(stat) return;

    pthread_join(eventThread, NULL);

}

void *eventCheckingThread(JNIEnv *env, jobject obj, jstring shared_path){

    READ_EVENT_TASK_LOOP = true;

    jclass ceClass = (*env)->GetObjectClass(env, obj);

    jmethodID onEventId = (*env)->GetMethodID(env, ceClass, "onEvent", "(Lcom/yacine_app/bixby_map/event/InputEventStruct;)V");

    jclass inputEventClass = (*env)->FindClass(env, "com/yacine_app/bixby_map/event/InputEventStruct");
    jobject inputEventObject = (*env)->GetObjectClass(env, inputEventClass);
    jmethodID inputEventConstructor = (*env)->GetMethodID(env, inputEventObject, "<init>", "()V");
    jobject inputEventInstance = (*env)->NewObject(env, inputEventClass, inputEventConstructor);
    jfieldID inputEventInstanceTimeval = (*env)->GetFieldID(env, inputEventClass, "timeval", "Lcom/yacine_app/bixby_map/event/Timeval;");
    jfieldID inputEventInstanceType = (*env)->GetFieldID(env, inputEventClass, "type", "S");
    jfieldID inputEventInstanceCode = (*env)->GetFieldID(env, inputEventClass, "code", "S");
    jfieldID inputEventInstanceValue = (*env)->GetFieldID(env, inputEventClass, "value", "I");

    jclass timevalClass = (*env)->FindClass(env, "com/yacine_app/bixby_map/event/Timeval");
    jobject timevalObject = (*env)->GetObjectClass(env, timevalClass);
    jmethodID timevalInstanceId = (*env)->GetMethodID(env, timevalObject, "<init>", "()V");
    jobject timevalInstance = (*env)->NewObject(env, timevalClass, timevalInstanceId);
    jfieldID timevalInstanceTv_sec = (*env)->GetFieldID(env, timevalClass, "tv_sec", "I");
    jfieldID timevalInstanceTv_usec = (*env)->GetFieldID(env, timevalClass, "tv_usec", "I");

    const char *path = (*env)->GetStringUTFChars(env, shared_path, null);

    struct input_event in;
    struct pollfd fds[1];

    char *full_exist_hash = (char*) malloc(500 * sizeof(char));

    sprintf(full_exist_hash, "%s/%s", path, INPUT_CUSTOM_EVENT_END_HASH);

    mkfifo(full_exist_hash, O_CREAT | S_IWUSR | S_IRUSR | S_IRGRP | S_IWOTH | S_IROTH | S_IWOTH);
    int end_hash_fd = open(full_exist_hash, O_NONBLOCK | O_WRONLY);

    if(end_hash_fd < 0){
        printf("Error with reading %s\n", full_exist_hash);
        _exit(-1);
    }

    char *full_path = (char*) malloc(500 * sizeof(char));

    sprintf(full_path, "%s/%s", path, INPUT_CUSTOM_EVENT_FIFIO_FILE);

    mkfifo(full_path, O_CREAT | S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);

    fds[0].fd = open(full_path, O_RDONLY);

    if(fds[0].fd < 0){
        printf("Error with reading %s\n", full_path);
        _exit(-1);
    }

    fds[0].events = POLLIN;

    while (READ_EVENT_TASK_LOOP) {
        poll(fds, 1, 5000);
        if(fds[0].revents){
            read(fds[0].fd, &in, sizeof(in));
            if(in.code | in.type | in.value > 0) {
                (*env)->SetIntField(env, timevalInstance, timevalInstanceTv_sec, in.time.tv_sec);
                (*env)->SetIntField(env, timevalInstance, timevalInstanceTv_usec, in.time.tv_usec);
                (*env)->SetObjectField(env, inputEventInstance, inputEventInstanceTimeval, timevalInstance);
                (*env)->SetShortField(env, inputEventInstance, inputEventInstanceType, in.type);
                (*env)->SetShortField(env, inputEventInstance, inputEventInstanceCode, in.code);
                (*env)->SetIntField(env, inputEventInstance, inputEventInstanceValue, in.value);
                (*env)->CallVoidMethod(env, obj, onEventId, inputEventInstance);
            }
                //printf("time: %li, code: %hi, type: %hi, value: %d\n\r", in.time.tv_sec, in.code, in.type, in.value);
        }
    }

    write(end_hash_fd, INPUT_CUSTOM_EVENT_END_HASH, strlen(INPUT_CUSTOM_EVENT_END_HASH));
    close(end_hash_fd);
    close(fds[0].fd);

}

Java 代码:

import android.util.Log;

public class CustomEvent {

    public static CustomEvent newInstance(){
        return new CustomEvent();
    }

    private String path;
    private OnEventListener onEventListener;
    private OnReleasedListener onReleasedListener;
    private OnStartedListener onStartedListener;

    public void setPath(String path) {
        this.path = path;
    }

    public void setOnEventListener(OnEventListener onEventListener) { this.onEventListener = onEventListener; }

    public void setOnReleasedListener(OnReleasedListener onReleasedListener) { this.onReleasedListener = onReleasedListener; }

    public void setOnStartedListener(OnStartedListener onStartedListener) { this.onStartedListener = onStartedListener; }

    private CustomEvent(){ }

    static {
        System.loadLibrary("LinuxEventReader");
    }

    private void onEvent(InputEventStruct inputEvent){
        if(this.onEventListener != null) this.onEventListener.onEvent(inputEvent);
    }

    private void onError(String err){
        Log.e("ERROR", err);
    }

    private native void listenTo(String path);
    private native void releaseListener();

    public void startListening(){
        new Thread(()->{
            if(this.onStartedListener != null) this.onStartedListener.onStarted();
            listenTo(path);
        }).start();
    }

    public void release(){
        new Thread(()->{
            releaseListener();
            if(this.onReleasedListener != null) this.onReleasedListener.onReleased();
        }).start();
    }

}

问题是,当这个程序正在执行时,它工作正常,但是当我尝试退出循环时,它在返回到 java 代码之前崩溃了

A/libc: Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x74e8995000 in tid 14435 (Thread-7), pid 13933 (e_app.bixby_map)

这就是我得到的错误。有什么帮助吗?

【问题讨论】:

  • 有两件事情很奇怪:1) 看起来您正在跨不同线程共享JNIEnv*,这是您不应该做的。 2) 在eventCheckingThread 的末尾没有return 语句,即使它应该返回void *
  • 我相信@Michael 给出了正确的理由; eventThread 不能使用由不同 (Java) 线程传递的 env。您可以轻松避免这种混乱:只需从 Java_com_yacine_1app_bixby_1map_event_CustomEvent_listenTo()(本机 Java 方法)调用(同步)eventCheckingThread() 函数。
  • AttachCurrentThread 让它工作需要更多时间,但在java代码中创建线程更准确

标签: c android-studio cmake android-ndk java-native-interface


【解决方案1】:

感谢@Michael@Alex Cohn 的有益评论。

@Michael 说完之后,我从 oracle 和 JavaVM and JNIEnvThreads 重新阅读了 JNI Documentation,我注意到我之前没有仔细阅读它们的错误,所以我使用 AttachCurrentThread 来实现我想要的描述,但是在@Alex Cohn 说之后,我决定在java代码中制作线程,它更容易处理和调试,这是最终代码:

C 代码:

static boolean READ_EVENT_TASK_LOOP = false;
static char * path = NULL;
static struct pollfd fds[1];

void *Java_com_yacine_1app_bixby_1map_event_CustomEvent_releaseListener(__unused JNIEnv *env, __unused jobject obj){

    READ_EVENT_TASK_LOOP = false;
    char *full_path = (char*) malloc(500 * sizeof(char));
    sprintf(full_path, "%s/%s", path, INPUT_CUSTOM_EVENT_FIFIO_FILE);
    remove(full_path);
    close(fds[0].fd);
    path = NULL;
    //free(fds);
    //_exit(0);
    return NULL;
}

void *Java_com_yacine_1app_bixby_1map_event_CustomEvent_listenTo(JNIEnv *env, jobject obj) {

    if(!READ_EVENT_TASK_LOOP) return NULL;

    struct input_event in;
    char *full_path = (char*) malloc(500 * sizeof(char));

    sprintf(full_path, "%s/%s", path, INPUT_CUSTOM_EVENT_FIFIO_FILE);
    mkfifo(full_path, O_CREAT | S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);

    jclass ceClass = (*env)->GetObjectClass(env, obj);
    jmethodID onEventId = (*env)->GetMethodID(env, ceClass, "onEvent", "(Lcom/yacine_app/bixby_map/event/InputEventStruct;)V");

    jclass inputEventClass = (*env)->FindClass(env, "com/yacine_app/bixby_map/event/InputEventStruct");
    jobject inputEventObject = (*env)->GetObjectClass(env, inputEventClass);
    jmethodID inputEventConstructor = (*env)->GetMethodID(env, inputEventObject, "<init>", "()V");
    jobject inputEventInstance = (*env)->NewObject(env, inputEventClass, inputEventConstructor);
    jfieldID inputEventInstanceTimeval = (*env)->GetFieldID(env, inputEventClass, "timeval", "Lcom/yacine_app/bixby_map/event/Timeval;");
    jfieldID inputEventInstanceType = (*env)->GetFieldID(env, inputEventClass, "type", "S");
    jfieldID inputEventInstanceCode = (*env)->GetFieldID(env, inputEventClass, "code", "S");
    jfieldID inputEventInstanceValue = (*env)->GetFieldID(env, inputEventClass, "value", "I");

    jclass timevalClass = (*env)->FindClass(env, "com/yacine_app/bixby_map/event/Timeval");
    jobject timevalObject = (*env)->GetObjectClass(env, timevalClass);
    jmethodID timevalInstanceId = (*env)->GetMethodID(env, timevalObject, "<init>", "()V");
    jobject timevalInstance = (*env)->NewObject(env, timevalClass, timevalInstanceId);
    jfieldID timevalInstanceTv_sec = (*env)->GetFieldID(env, timevalClass, "tv_sec", "I");
    jfieldID timevalInstanceTv_usec = (*env)->GetFieldID(env, timevalClass, "tv_usec", "I");

    fds[0].fd = open(full_path, O_RDONLY);
    if(fds[0].fd < 0){
        printf("Error with reading %s\n", full_path);
        return NULL;
    }

    fds[0].events = POLLIN;

    //pthread_cleanup_push(cleanup, end_hash_fd)

    while (READ_EVENT_TASK_LOOP) {
        poll(fds, 1, 500);
        if(fds[0].revents){
            read(fds[0].fd, &in, sizeof(in));
            if(in.code | in.type | in.value > 0) {
                (*env)->SetIntField(env, timevalInstance, timevalInstanceTv_sec, in.time.tv_sec);
                (*env)->SetIntField(env, timevalInstance, timevalInstanceTv_usec, in.time.tv_usec);
                (*env)->SetObjectField(env, inputEventInstance, inputEventInstanceTimeval, timevalInstance);
                (*env)->SetShortField(env, inputEventInstance, inputEventInstanceType, in.type);
                (*env)->SetShortField(env, inputEventInstance, inputEventInstanceCode, in.code);
                (*env)->SetIntField(env, inputEventInstance, inputEventInstanceValue, in.value);
                (*env)->CallVoidMethod(env, obj, onEventId, inputEventInstance);
            }
            //printf("time: %li, code: %hi, type: %hi, value: %d\n\r", in.time.tv_sec, in.code, in.type, in.value);
        }
    }

    //close(fds[0].fd);
    return NULL;
}

void *Java_com_yacine_1app_bixby_1map_event_CustomEvent_initialize(JNIEnv *env, __unused jobject obj, jstring shared_path) {
    READ_EVENT_TASK_LOOP = true;
    path = (char*) (*env)->GetStringUTFChars(env, shared_path, NULL);
    return NULL;
}

Java 代码:

public class CustomEvent {

    public static CustomEvent newInstance(){ return new CustomEvent(); }

    private boolean initialized = false;
    private String path;
    private OnEventListener onEventListener;
    private OnReleasedListener onReleasedListener;
    private OnStartedListener onStartedListener;
    private RootManager rm;
    private Thread startThread, stopThread;
    private final Runnable start = ()->{
        try {
            this.rm.exec("./PsB -d 6 -o " + this.path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(this.onStartedListener != null) this.onStartedListener.onStarted();
        this.listenTo();
    }, stop = ()->{
        this.releaseListener();
        this.startThread = null;
        this.stopThread = null;
        try {
            this.rm.exit();
            this.rm = null;
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        this.initialized = false;
        if(this.onReleasedListener != null) this.onReleasedListener.onReleased();
    };

    static {
        System.loadLibrary("LinuxEventReader");
    }

    private CustomEvent(){ }

    public void init(String path){
        this.path = path;
        this.initialize(this.path);
        this.rm = new RootManager();
        if(this.rm.isRootAccess())return;
        try {
            this.rm.setDir(this.path + "/files/exec");
            this.rm.requestRoot();
            this.rm.exec("chmod +x PsB");
            this.startThread = new Thread(this.start);
            this.stopThread = new Thread(this.stop);
            this.initialized = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void startListening(){
        if(!this.initialized) throw new IllegalStateException();
        this.startThread.start();
    }

    public void release(){
        if(!this.initialized) throw new IllegalStateException();
        this.stopThread.start();
    }

    public void setOnEventListener(OnEventListener onEventListener) { this.onEventListener = onEventListener; }
    public void setOnReleasedListener(OnReleasedListener onReleasedListener) { this.onReleasedListener = onReleasedListener; }
    public void setOnStartedListener(OnStartedListener onStartedListener) { this.onStartedListener = onStartedListener; }

    @SuppressWarnings("unused")
    private synchronized void onEvent(InputEventStruct inputEvent){
        if(this.onEventListener != null) this.onEventListener.onEvent(inputEvent);
    }
    @SuppressWarnings("unused")
    private void onError(String err){
        Log.e("ERROR", err);
    }

    private native void listenTo();
    private native void releaseListener();
    private native void initialize(String path);

}

如果我做的任何事情是错误的或不准确的,我会感谢更多帮助:)

【讨论】:

    猜你喜欢
    • 2013-03-21
    • 2013-11-10
    • 1970-01-01
    • 2015-09-29
    • 1970-01-01
    • 2016-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多