Android Studio 串口jni开发

来源:互联网 发布:杭州橙速网络怎么样 编辑:程序博客网 时间:2024/06/06 09:14

1. 开发环境

Mac OS X , Android Studio 2.1.2


2. 创建新工程

创建SerialPortDemo工程, Minimum SDK 选择 API 19: Android 4.4。



3. 工程环境

ndk 路径使用默认的, gradle version 2.10





4. 切换工程显示方式



5.修改build.gradle

注意是与app目录同级的build.gradle

修改classpath 对应的版本,请参考:https://sites.google.com/a/android.com/tools/tech-docs/new-build-system/gradle-experimental#TOC-Standalone-NDK-Plugin

gradle version 2.10 目前对应  "com.android.tools.build:gradle-experimental:0.7.0-alpha1"



06.修改app的build.gradle

注意:是app目录下的build.gradle

新的gradle 参考:https://sites.google.com/a/android.com/tools/tech-docs/new-build-system/gradle-experimental#TOC-Standalone-NDK-Plugin

请注意红框内的,否则会出现以下一些错误:

Error:Cause: org.gradle.api.internal.ExtensibleDynamicObject

Error:No signature of method: org.gradle.model.ModelMap.getDefaultProguardFile() is applicable for argument types: (java.lang.String) values: [proguard-android.txt]




07.创建jni目录



SerialPort.h

#include <jni.h>#ifndef _Included_com_imlaidian_serialportdemo_serialapi_SerialPort#define _Included_com_imlaidian_serialportdemo_serialapi_SerialPort#ifdef __cplusplusextern "C" {#endif// 命名格式: java_包名_类目录_类名_接口JNIEXPORT jobject JNICALL Java_com_imlaidian_serialportdemo_serialapi_SerialPort_open  (JNIEnv *, jclass, jstring, jint, jint);JNIEXPORT void JNICALL Java_com_imlaidian_serialportdemo_serialapi_SerialPort_close  (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif

SerialPort.c

/* * Copyright 2009-2011 Cedric Priscal * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#include <jni.h>#include "termios.h"#include "SerialPort.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;}}/* * Class:     android_serialport_SerialPort * Method:    open * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */JNIEXPORT jobject JNICALL Java_com_imlaidian_serialdemo_SerialPort_SerialPort_open  (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags){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 with flags 0x%x", path_utf, O_RDWR | flags);fd = open(path_utf, O_RDWR | flags);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;}/* * Class:     cedric_serial_SerialPort * Method:    close * Signature: ()V */JNIEXPORT void JNICALL Java_com_imlaidian_serialdemo_SerialPort_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);}



由于api 19 之后的 termios.h 里面的函数有调整,因此调试过程中,出现

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "tcgetattr" referenced by "libserialport.so"...

解决方法:

将api 19  的 termios.h 拷贝到 jni 目录下

termios.h

/* * Copyright (C) 2008 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: *  * Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. *  * Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in *    the documentation and/or other materials provided with the *    distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef _TERMIOS_H_#define _TERMIOS_H_#include <sys/cdefs.h>#include <sys/ioctl.h>#include <sys/types.h>#include <stdint.h>#include <linux/termios.h>__BEGIN_DECLS/* Redefine these to match their ioctl number */#undef  TCSANOW#define TCSANOW    TCSETS#undef  TCSADRAIN#define TCSADRAIN  TCSETSW#undef  TCSAFLUSH#define TCSAFLUSH  TCSETSFstatic __inline__ int tcgetattr(int fd, struct termios *s){    return ioctl(fd, TCGETS, s);}static __inline__ int tcsetattr(int fd, int __opt, const struct termios *s){    return ioctl(fd, __opt, (void *)s);}static __inline__ int tcflow(int fd, int action){    return ioctl(fd, TCXONC, (void *)(intptr_t)action);}static __inline__ int tcflush(int fd, int __queue){    return ioctl(fd, TCFLSH, (void *)(intptr_t)__queue);}static __inline__ pid_t tcgetsid(int fd){    pid_t _pid;    return ioctl(fd, TIOCGSID, &_pid) ? (pid_t)-1 : _pid;}static __inline__ int tcsendbreak(int fd, int __duration){    return ioctl(fd, TCSBRKP, (void *)(uintptr_t)__duration);}static __inline__ speed_t cfgetospeed(const struct termios *s){    return (speed_t)(s->c_cflag & CBAUD);}static __inline__ int cfsetospeed(struct termios *s, speed_t  speed){    s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD);    return 0;}static __inline__ speed_t cfgetispeed(const struct termios *s){    return (speed_t)(s->c_cflag & CBAUD);}static __inline__ int cfsetispeed(struct termios *s, speed_t  speed){    s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD);    return 0;}static __inline__ void cfmakeraw(struct termios *s){    s->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);    s->c_oflag &= ~OPOST;    s->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);    s->c_cflag &= ~(CSIZE|PARENB);    s->c_cflag |= CS8;}__END_DECLS#endif /* _TERMIOS_H_ */

此处参考:

http://jp1017.github.io/2016/06/30/jni-开发错误之-java-lang-UnsatisfiedLinkError-dlopen-failed-cannot-locate-symbol-tcgetattr-referenced-by-libserial-port-so/

http://www.eoeandroid.com/thread-914514-1-1.html



08.创建串口native接口类


SerialPort.java

/* * Copyright 2009 Cedric Priscal *  * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *  * http://www.apache.org/licenses/LICENSE-2.0 *  * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.  */package com.imlaidian.serialportdemo.serialapi;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, int flags) throws SecurityException, IOException {/* Check access permission */if (!device.canRead() || !device.canWrite()) {try {/* Missing read/write permission, trying to chmod the file */Process su;su = Runtime.getRuntime().exec("/system/bin/su");String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"+ "exit\n";su.getOutputStream().write(cmd.getBytes());if ((su.waitFor() != 0) || !device.canRead()|| !device.canWrite()) {throw new SecurityException();}} catch (Exception e) {e.printStackTrace();throw new SecurityException();}}mFd = open(device.getAbsolutePath(), baudrate, 8, 1,'n');if (mFd == null) {Log.e(TAG, "native open returns null");throw new IOException();}mFileInputStream = new FileInputStream(mFd);mFileOutputStream = new FileOutputStream(mFd);}// Getters and setterspublic InputStream getInputStream() {return mFileInputStream;}public OutputStream getOutputStream() {return mFileOutputStream;}// JNIprivate native static FileDescriptor open(String path, int baudrate, int databits,int stopbits,char parity);public native void close();static {System.loadLibrary("serialport"); // 调用jni生成的库}}


SerialPortFinder.java

/* * Copyright 2009 Cedric Priscal *  * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *  * http://www.apache.org/licenses/LICENSE-2.0 *  * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.  */package com.imlaidian.serialportdemo.serialapi;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.io.LineNumberReader;import java.util.Iterator;import java.util.Vector;import android.util.Log;public class SerialPortFinder {public class Driver {public Driver(String name, String root) {mDriverName = name;mDeviceRoot = root;}private String mDriverName;private String mDeviceRoot;Vector<File> mDevices = null;public Vector<File> getDevices() {if (mDevices == null) {mDevices = new Vector<File>();File dev = new File("/dev");File[] files = dev.listFiles();int i;for (i=0; i<files.length; i++) {if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {Log.d(TAG, "Found new device: " + files[i]);mDevices.add(files[i]);}}}return mDevices;}public String getName() {return mDriverName;}}private static final String TAG = "SerialPort";private Vector<Driver> mDrivers = null;Vector<Driver> getDrivers() throws IOException {if (mDrivers == null) {mDrivers = new Vector<Driver>();LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers"));String l;while((l = r.readLine()) != null) {// Issue 3:// Since driver name may contain spaces, we do not extract driver name with split()String drivername = l.substring(0, 0x15).trim();String[] w = l.split(" +");if ((w.length >= 5) && (w[w.length-1].equals("serial"))) {Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length-4]);mDrivers.add(new Driver(drivername, w[w.length-4]));}}r.close();}return mDrivers;}public String[] getAllDevices() {Vector<String> devices = new Vector<String>();// Parse each driverIterator<Driver> itdriv;try {itdriv = getDrivers().iterator();while(itdriv.hasNext()) {Driver driver = itdriv.next();Iterator<File> itdev = driver.getDevices().iterator();while(itdev.hasNext()) {String device = itdev.next().getName();String value = String.format("%s (%s)", device, driver.getName());devices.add(value);}}} catch (IOException e) {e.printStackTrace();}return devices.toArray(new String[devices.size()]);}public String[] getAllDevicesPath() {Vector<String> devices = new Vector<String>();// Parse each driverIterator<Driver> itdriv;try {itdriv = getDrivers().iterator();while(itdriv.hasNext()) {Driver driver = itdriv.next();Iterator<File> itdev = driver.getDevices().iterator();while(itdev.hasNext()) {String device = itdev.next().getAbsolutePath();devices.add(device);}}} catch (IOException e) {e.printStackTrace();}return devices.toArray(new String[devices.size()]);}}

最终目录结构如下



09. gradle sync同步




10. 生成.so 路径














0 2
原创粉丝点击