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 路径














更多相关文章

  1. 如何创建一个android的react-native组件(一)
  2. MediaRecorder创建Surface流程学习
  3. Android(安卓)ToolBar 解析与应用(一)创建toolbar
  4. Android(安卓)Studio升级后编辑local path 不存在的问题
  5. 四、android studio使用自己库编译工程
  6. Arcgis api for android V2.0模拟器调试问题
  7. Android多渠道打包修改资源文件
  8. Android(安卓)P 图形显示系统(十) BufferQueue(一)
  9. Android(安卓)studio新建项目之后由于gradle配置问题导致项目无

随机推荐

  1. 常用伪类选择器的实例演示及参数
  2. Android中ActionBar及Overflow的显示
  3. Android(安卓)Relative Layout 安卓相对
  4. Android(安卓)adb不是内部或外部命令 问
  5. 重新审视 Android
  6. Android(安卓)反编译apk 到java源码的方
  7. Android入门篇三:使用静态变量在Activity
  8. Android实现图表绘制和展示
  9. Android设计原则
  10. Android(安卓)系统用户态启动过程