无法解决相应的功能 [英] Cannot resolve corresponding jni fuction

查看:68
本文介绍了无法解决相应的功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个通过串行端口发送数据的应用程序.这需要从本地库调用方法 我已经使用ndk生成了.so库的两个本机方法"open"和"close",并将它们放在jnilibs文件夹中,但是仍然出现错误,提示无法解析相应的jni函数"

I am making an app to send data through serial port. this requires to call methos from a native library I have two native methods "open" "close" I have generated to .so libraries using ndk and put them in jnilibs folder but still it gives an error saying "cannot reolve corresponding jni function"

SerialPort.c

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

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类和本机文件.请帮忙

These are my java class and native file. Please do help

推荐答案

正如我解释在其他地方一样,不要指望Android Studio可以将本机方法声明神奇地解析到预构建的库中(即使已正确复制到 src/main/jnLibs 中).

As I explained elsewhere, don't expect Android Studio to resolve magically the native method declarations into a prebuilt library (even if it is correctly copied into src/main/jnLibs).

您可以简单地忽略此错误消息:您的APK仍将安装预构建的库,并且本机方法将在运行时解析.

You can simply ignore this error message: your APK will still install the prebuilt library, and the native method will be resolved at run time.

您可以为这些方法或整个类添加@SuppressWarnings("JniMissingFunction")批注:

You can add @SuppressWarnings("JniMissingFunction") annotation for these method, or for the entire class:

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

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

如果您可以在任何设备甚至模拟器上安装APK,并且已加载 SerialPort 类,则您的JNI包装器已正确配置.当系统无法加载本机库时,它将有用的错误消息写入 logcat .

If you can install the APK on any device, or even on emulator, and the SerialPort class is loaded, then your JNI wrapper is configured correctly. When the system fails to load a native library, it writes helpful error messages to logcat.

让我在" SerialPort 类已加载" 上进行扩展.在Java中,类加载器可能(应该应该")将加载类推迟到真正必要时才进行.因此,仅在APK中包含类将不会触发其静态构造函数.但是,如果您有一个字段

Let me expand a bit on "the SerialPort class is loaded". In Java, the classloader may (rather 'should') defer loading a class until it is really necessary. So, simply having the class in your APK will not trigger its static constructor. But if you have a field

private SerialPort m_serialPort = new SerialPort();

在您的MainActivity中,该类将被加载,并且即使您根本不触摸此 m_serialPort ,JNI也将被初始化.请注意,我在 SerialPort 类中添加了什么都不做的默认构造函数:

in your MainActivity, then the class will be loaded, and the JNI will be initialized even if you don't actually touch this m_serialPort at all. Note that I added an do-nothing default constructor to SerialPort class:

public SerialPort() {}

这不会测试JNI代码本身,例如参数的转换等.如果您没有可用于测试代码的真实设备,则应设计一些模拟接口,这些接口将充当实际的串行端口.

This does not test the JNI code itself, like conversion of the parameters and such. If you don't have a real device that can be used to test your code, you should design some mock interfaces that will play the role of the actual serial port.

这篇关于无法解决相应的功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆