在公司主要做VC++ 方面开发,最近公司新模块开发,主要用串口通讯,而且要求二次开发包,要VC、Linux和Android 三个平台的,但是由于Android 方面不关我事。但是,小弟是个新手,目前正在学习Android 开发(准备往这方面发展),于是乎,就自己私底下搞搞学习学习。因此,记录这一个多星期的一些收获,以后可以看看。

1、开发环境

(1) Android Studio 2.2.2      (2) JDK 1.8     (3) NDK  android-ndk-r10e (一般使用这个版本,低于这个版本的我调试都报错)

(4) 最重要的谷歌开源的串口类android-serialport-api  下载地址: http://code.google.com/p/android-serialport-api/  这个地址需要,也可以到GitHub或则百度大把。 下载源码之后发现源码可以在ADT使用。



2、创建SerialPortNDK工程

(1) 这个工程主要用户生成SerialPort类的动态连接库libserial_port.so文件。



(2) 选择编译SDK版本 API19



3、导入下载SerialPort类和SerialPortFinder类(文章末尾有两个类的源码)



4、使用javah 命令生成JNI的 .h头文件

(1)使用windows自带的命令行或则使用Android Studio打开 Terminal(View -> Tool Windows -> Terminal),进入JNI工程的根目录下,

使用执行javah -classpath . -jni SerialPort

这个步骤很容易出现找不到类文件的情况,如:

a、


b、


出现第一种,就是没有找到jni的根目录,第二种就是没有指定路径

记住通用格式——

javah -classpath . -jni 类路径.JNI类

 这个步骤需要注意的地方是什么才算工程的根目录,请看下面图的解释:


看文字可能有点难理解,举个例子,那我当前的工程目录来说:

E:\androidcode\SerialPortNDK\app\src\main\java\com\serialport

则根目录就是E:\androidcode\SerialPortNDK\app\src\main\java ---》 java就是根目录

其实,还有个窍门,直接看.java类文件的包名也可以知道,如:查看SerialPort.java 文件

上图中包名为 com.serialport 则包名的上一层目录就是根目录。


因此,执行在java目录,执行javah -classpath . -jni com.serialport.SerialPort 如图:



可以看到在java 目录下出现jni 头文件:



PC文件夹下:

5、在工程中建立JNI文件夹,添加.c文件

(1) 在工程的java文件下右键 jni 文件夹,如图
(2) 将右键菜单中选择New -> C/C++ Source File创建与 .h 文件同名的 .c 文件

com_serialport_SerialPort.h 文件移动到jni目录下,并且生成同名的.c文件( com_serialport_SerialPort.c )

如图:





特别需要注意的地方是: (3)在.c文件中,open和close 名字必须与.h文件的名字一样 如图:



(4)并且#include "xxx.h"名字必须和jni.h文件名一样,否则编译将不通过。


上面两个注意的地方可以去查看c/c++的规则
6、修改和配置相关文件生成 动态库.so 文件 (1) 在工程目录下的 local.properties文件, 添加 ndk 路径, 如图,我的ndk路径

Android-ndk的版本至少要 r9d 以上的版本,如果等于或低于该版本,运行时会提示No rule to make target的错误。我使用的是 r10e 的版本。

(2) 在 module 的 build.gradle 中添加 ndk 配置 在 defaultConfig 节点下添加:
        ndk{            //abiFilter "armeabi"           //过滤器,生成相关平台的.so文件, 不写,则生成每种平台相关的.so文件            moduleName "serial_port"        // 生成so的名称            ldLibs "log", "z", "m", "jnigraphics", "android"        }
如图:

(3) 在 工程的gradle.properties 文件中添加
android.useDeprecatedNdk=true
否则 sync 时会提示错误  NDK integration is deprecated in the current plugin.

(4) Sync Project with Gradle Files 或则Rebuild Project整个工程,则可以生成我们需要的libserial_port.so动态库了。 生成的libserial_port.so 目录在E:\androidcode\SerialPortNDK\app\build\intermediates\ndk\debug\lib


终于成功了,初学什么都不懂,整整搞了一个星期,每天下班后都在弄,皇天不负有心人。。。。。

后记: 由于api 19 之后的 termios.h 里面的函数有调整,因此调试过程中,有可能出现
java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "tcgetattr" referenced by "libserialport.so"...

解决方法:

将api 19 的 termios.h 拷贝到 jni 目录下
termios.h
详情请查看 http://jp1017.github.io/2016/06/30/jni-%E5%BC%80%E5%8F%91%E9%94%99%E8%AF%AF%E4%B9%8B-java-lang-UnsatisfiedLinkError-dlopen-failed-cannot-locate-symbol-tcgetattr-referenced-by-libserial-port-so/

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


上面主要使用到的类:

(1) SerialPort.class

/* * 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.serialport;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, flags);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 flags);public native void close();static {System.loadLibrary("serial_port");}}

(2) SerialPortFinder.class

/* * 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.serialport;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 mDevices = null;public Vector getDevices() {if (mDevices == null) {mDevices = new Vector();File dev = new File("/dev");File[] files = dev.listFiles();int i;for (i=0; i mDrivers = null;Vector getDrivers() throws IOException {if (mDrivers == null) {mDrivers = new Vector();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 devices = new Vector();// Parse each driverIterator itdriv;try {itdriv = getDrivers().iterator();while(itdriv.hasNext()) {Driver driver = itdriv.next();Iterator 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 devices = new Vector();// Parse each driverIterator itdriv;try {itdriv = getDrivers().iterator();while(itdriv.hasNext()) {Driver driver = itdriv.next();Iterator 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()]);}}

(3)  com_serialport_SerialPort.h 

/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class com_serialport_SerialPort */#ifndef _Included_com_serialport_SerialPort#define _Included_com_serialport_SerialPort#ifdef __cplusplusextern "C" {#endif/* * Class:     com_serialport_SerialPort * Method:    open * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */JNIEXPORT jobject JNICALL Java_com_serialport_SerialPort_open  (JNIEnv *, jclass, jstring, jint, jint);/* * Class:     com_serialport_SerialPort * Method:    close * Signature: ()V */JNIEXPORT void JNICALL Java_com_serialport_SerialPort_close  (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif

(3)  com_serialport_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 #include #include #include #include #include #include #include "com_serialport_SerialPort.h.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_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, "", "()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_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);}







参考教程(都是大神,所以非常感激):

http://blog.csdn.net/caojengineer/article/details/51838394

http://blog.csdn.net/u010302327/article/details/50224457

http://blog.csdn.net/doris_d/article/details/53330838

 

更多相关文章

  1. Android(安卓)studio简单使用JNI实例
  2. Android打包AAR及与unity通信方法
  3. Android学习总结
  4. Android(安卓)动态加载布局文件
  5. Windows下的Android(安卓)SDK下载,2.2之前各个版本及Google API,文
  6. android 应用程序包文件 (APK)
  7. 页面调用ADB操作Android设备
  8. Android(安卓)adb不是内部或外部命令 问题解决
  9. NPM 和webpack 的基础使用

随机推荐

  1. Android(安卓)关于RatingBar评分条
  2. android的PreferenceActivity
  3. Android(安卓)ORM 框架之 Android中ORMLi
  4. android布局属性
  5. 在 Eclipse 中导入 Android(安卓)示例程
  6. 经典的大牛博客推荐
  7. Android屏幕元素层次结构
  8. Android(安卓)ApiDemos 系列解析【View-I
  9. Android(安卓)-Recovery
  10. Android(安卓)SDK需要安装的包