安卓基于谷歌串口api进行串口开发
准备材料
AndroidStudio
谷歌android-serialport-api
前情提要
网上提供很多基于c语言对安卓串口开发,有jni、cmake等等,不过都太高深,谷歌提供的api已经可以满足基本读写(对数据位、停止位、校验位无要求,默认N81),这也是最简单的串口开发。
Java并不是直接调用c中的函数去和串口交互,而是用cmake或jni编译c得到的so库文件实现的,所以使用谷歌api不用再去把他的c文件之类的拿来,只用把他的so库文件拿来用即可,也不用知道so文件怎么用,谷歌demo中写好了直接调用so文件的java类,所以最终我们只需要导入需要的东西然后在谷歌串口类之上编写就行了。
开始骚操作
- 首先解压谷歌api demo放一边备用;
- 新建AS项目,一路next,拿到一个空空的自带界面的项目;
- 打开项目目录app下的build.gradle,在buildTypes同级设置so文件的lib位置,这里用的自己生成的libs文件夹
android { compileSdkVersion 28 defaultConfig { applicationId "com.example.hp.demo" minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets.main{ jniLibs.srcDirs = ['libs'] } }
- 打开谷歌api对应的lib文件夹android-serialport-api\android-serialport-api\project\libs,将里面的三种CPU架构用的so文件夹全复制过去
- 打开谷歌api的源代码目录android-serialport-api\android-serialport-api\project\src,内含谷歌api串口开发地基,将该文件夹android_serialport_api直接复制到自己项目java文件夹内(和com同级)
- android_serialport_api这个文件夹包含三件物品:
sample:谷歌api DEMO所用演示代码,移植到自己项目后直接删除,不然会和xml接不上报错
SerialPort.java:我们直接接触的对串口开发操作的类,串口读写都基于此,由该文件和so文件对接,我们的操作和该文件对接
SerialPortFinder.java:一般用不到(我没用到),可以找到并输出所有可用串口名和路径,可能是用于对串口批量操作的,一两个串口的用不到
- 在此再次说一下:正常大神操作是,先写c文件和h文件,然后jni或cmake编译得出so文件,然后写调用so文件的java类,然后再调用该java类进行各种操作;
我们的操作是上述操作的最后一步,直接用谷歌大佬写好的c编译好的so写好的java类,在这之上写我们自己的操作
- 这时就已经可以准备自己的操作了,在此之前检查几个地方:
1)第3点是否写好
2)SerialPort.java中的末尾有这么一段代码:
static { System.loadLibrary("serial_port"); }
这就是传说中调用so库的地方,第三点没写好是找不到这个库的,此外(“”)中的库名也要和so文件对应,即so文件的libserial_port.so去掉开头lib就是库名
- 我们的骚操作刚刚开始:
1) 写一个基于SerialPort.java封装好的串口类,虽然已经可以直接用SerialPort.java,但是SerialPort.java中的方法还是稍微有点基层,当项目里有对多个串口进行不同的操作时这一步是必要的,不然代码太重复繁琐,封装的串口类应该有这几个功能:
从外部设置串口号、波特率
向外部传递串口返回报文
打开串口,获取输入输出流
关闭串口,关闭输入输出流
向串口写入字节*
向串口写入字节数组*
向串口写入字符串*
实时监听串口,获取完整的报文
具体代码见Demo
2) 使用串口类,new一个你做的串口类,注册好串口类的数据接收器,在需要串口发送数据的地方调用串口类的发送方法,在接收器中做好对报文的校验等处理
踩坑预警
- 串口返回报文残缺,中间少那么一两个字节
因为你注册了两个对同一个串口的监听器,他俩在互抢
- 串口返回报文从中间断开
串口另一头发送的时候可能是一个字节一个字节发送的,或者一段一段发送的,不过都会在很短的时间全给你发过来,这时候最保险的做法就是收到数据时,睡上10~100ms,再读一次(接收数据实际上是你从对串口的输入流缓冲区中取出数据),然后拼上返回给外部
- New一个串口太吃资源
接收数据的地方其实是一个循环读取的线程,读取一次让它睡10~100ms再循环
- 关闭串口之后该串口读取线程还在执行
把读取线程run()里的内容拿while(){ }括起来,while条件里扔一个布尔型,默认符合条件,关闭串口时改了变量让while不符合条件,线程就自然销毁了
- SerialPort.java类new的时候除了串口文件和波特率之外,还要一个int型flags参数
这个我也没搞明白,学长用的时候都是写死了0传过去,没发现出问题
- 读取数据时inputStream.read() 方法
该方法返回值是个int,表示读到的字节数组长度,所以读的时候拿个int接着,当读完缓冲区的数据后会返回-1,所以读了之后有无数据都有返回值,所以对返回数据进一步操作前用个if括起来,int大于0再执行;
该方法传递参数有几种不同形式:
1) 传入一个byte[]容器,读到的数据存入该容器,注意不要空指针,不要超出容器容量
2) 传入一个byte[]容器,一个int型起始位置off,一个int型数据长度len,off表示读到的数据从容器的byte[off]位置开始存,len表示读len个字节存进容器,这种传参在第二次读取拼接第一次报文的地方会用到