Не удается разрешить соответствующую функцию JNI - PullRequest
0 голосов
/ 20 мая 2018

Я делаю приложение для отправки данных через последовательный порт.это требует вызова методов из нативной библиотеки. У меня есть два нативных метода: «открыть», «закрыть», которые я сгенерировал для библиотек .so с помощью ndk, и поместить их в папку jnilibs, но все равно выдает ошибку, говорящую «не удается восстановить соответствующую функцию jni»

SerialPort.c

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>

#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

static speed_t getBaudrate(jint baudrate)
{
    switch(baudrate) {
    case 0: return B0;
    case 50: return B50;
    case 75: return B75;
    case 110: return B110;
    case 134: return B134;
    case 150: return B150;
    case 200: return B200;
    case 300: return B300;
    case 600: return B600;
    case 1200: return B1200;
    case 1800: return B1800;
    case 2400: return B2400;
    case 4800: return B4800;
    case 9600: return B9600;
    case 19200: return B19200;
    case 38400: return B38400;
    case 57600: return B57600;
    case 115200: return B115200;
    case 230400: return B230400;
    case 460800: return B460800;
    case 500000: return B500000;
    case 576000: return B576000;
    case 921600: return B921600;
    case 1000000: return B1000000;
    case 1152000: return B1152000;
    case 1500000: return B1500000;
    case 2000000: return B2000000;
    case 2500000: return B2500000;
    case 3000000: return B3000000;
    case 3500000: return B3500000;
    case 4000000: return B4000000;
    default: return -1;
    }
}

JNIEXPORT jobject JNICALL Java_com_example_richyrony_serial485_SerialPort_open(JNIEnv *env, jobject thiz, jstring path, jint baudrate)
{
    int fd;
    speed_t speed;
    jobject mFileDescriptor;

    /* Check arguments */
    {
        speed = getBaudrate(baudrate);
        if (speed == -1) {
            /* TODO: throw an exception */
            LOGE("Invalid baudrate");
            return NULL;
        }
    }

    /* Opening device */
    {
        jboolean iscopy;
        const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
        LOGD("Opening serial port %s", path_utf);
        LOGD(" serial port buadrate %s", speed);
//      fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);
        fd = open(path_utf,O_RDWR | O_SYNC);               //2011.8.26 
        LOGD("open() fd = %d", fd);
        (*env)->ReleaseStringUTFChars(env, path, path_utf);
        if (fd == -1)
        {
            /* Throw an exception */
            LOGE("Cannot open port");
            /* TODO: throw an exception */
            return NULL;
        }
    }

    /* Configure device */
    {
        struct termios cfg;
        LOGD("Configuring serial port");
        if (tcgetattr(fd, &cfg))
        {
            LOGE("tcgetattr() failed");
            close(fd);
            /* TODO: throw an exception */
            return NULL;
        }

        cfmakeraw(&cfg);
        cfsetispeed(&cfg, speed);
        cfsetospeed(&cfg, speed);

        if (tcsetattr(fd, TCSANOW, &cfg))
        {
            LOGE("tcsetattr() failed");
            close(fd);
            /* TODO: throw an exception */
            return NULL;
        }
    }

    /* Create a corresponding file descriptor */
    {
        jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
        jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
        jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
        mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
        (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
    }

    return mFileDescriptor;
}

void  Java_com_example_richyrony_serial485_SerialPort_close
  (JNIEnv *env, jobject thiz)
{
    jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
    jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");

    jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
    jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");

    jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
    jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);

    LOGD("close(fd = %d)", descriptor);
    close(descriptor);
}

SerialPort.Java

package com.example.richyrony.serial485;


import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.util.Log;

public class SerialPort {

    private static final String TAG = "SerialPort";

    /*
     * Do not 

remove or rename the field mFd: it is used by native method close();
     */
    private FileDescriptor mFd;
    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate) throws SecurityException, IOException {

    /* Check access permission */
    if (!device.canRead() || !device.canWrite()) {
        try {

            if (!device.canRead() || !device.canWrite()) {
                throw new SecurityException();
            } 
        }catch (Exception e) {
                e.printStackTrace();
                throw new SecurityException();
        }
    }



    mFd = open(device.getAbsolutePath(), baudrate);
    if (mFd == null) {
        Log.e(TAG, "native open returns null");
        throw new IOException();
    }
    mFileInputStream = new FileInputStream(mFd);
    mFileOutputStream = new FileOutputStream(mFd);
}

// Getters and setters
public InputStream getInputStream() {
    return mFileInputStream;
}

public OutputStream getOutputStream() {
    return mFileOutputStream;
}

// JNI
static {
    System.loadLibrary("serial_port");

}

private native static FileDescriptor open(String path, int baudrate);
public native void close();

}

Это мой класс Java и собственный файл.Пожалуйста, помогите

1 Ответ

0 голосов
/ 20 мая 2018

Как я объяснил в другом месте , не ожидайте, что Android Studio волшебным образом разрешит объявления собственных методов в предварительно собранную библиотеку (даже если она правильно скопирована в src / main / jnLibs ).

Вы можете просто проигнорировать это сообщение об ошибке: ваш APK все равно установит предварительно собранную библиотеку, и собственный метод будет разрешен во время выполнения.

Вы можете добавить @SuppressWarnings("JniMissingFunction") аннотацию для этого методаили для всего класса:

@SuppressWarnings("JniMissingFunction")
private native static FileDescriptor open(String path, int baudrate);

@SuppressWarnings("JniMissingFunction")
public native void close();

Если вы можете установить APK на любом устройстве или даже на эмуляторе, и класс SerialPort загружен, то ваша оболочка JNI настроенаправильно.Когда система не может загрузить собственную библиотеку, она записывает полезные сообщения об ошибках в logcat .

Позвольте мне немного расширить на "класс SerialPort загружен ".В Java загрузчик классов может (скорее «должен») отложить загрузку класса до тех пор, пока это не станет действительно необходимым.Таким образом, просто наличие класса в вашем APK не вызовет его статический конструктор.Но если у вас есть поле

private SerialPort m_serialPort = new SerialPort();

в вашей MainActivity, тогда класс будет загружен, и JNI будет инициализирован, даже если вы вообще не трогаете этот m_serialPort ,Обратите внимание, что я добавил конструктор по умолчанию без * в класс SerialPort :

public SerialPort() {}

Это не проверяет сам код JNI, как преобразование параметров и тому подобное.Если у вас нет реального устройства, которое можно было бы использовать для тестирования вашего кода, вам следует разработать несколько фиктивных интерфейсов, которые будут играть роль реального последовательного порта.

...