环境:android studio3.5 , java8 , 串口通讯设备(刷卡机)一台
场景:android系统(非手机)的设备上外接一个刷卡机,读取卡号
介绍:android 实现设备的刷卡操作 监听 、处理(打印卡号)。(参考 google 官方demo)
android studio 先连接设备, adb connect 设备ip:端口
1 引入
权限:
<uses-feature android:name="android.hardware.nfc" android:required="true"/> <uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
包:
implementation 'com.aill:AndroidSerialPort:1.0.8'
implementation files('libs/eventbus-3.0.0.jar')
2 复制 。目录结构如下(目录结构还得按照官方的路径),package com.aill.androidserialport;
(1)SerialPort
package com.aill.androidserialport;
import android.text.TextUtils;
import android.util.Log;
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;
/**
* @description 串口操作类
*/
public class SerialPort {
private static final String TAG = "SerialPort";
/**
* 不要删除或重命名字段mFd:原生方法close()使用了该字段
*/
private FileDescriptor mFd;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
/**
* 有些设备su路径是/system/xbin/su
*/
private static String mSuPath = "/system/bin/su";
static {
System.loadLibrary("android_serial_port");
}
public static void setSuPath(String suPath) {
if (TextUtils.isEmpty(suPath)) {
return;
}
mSuPath = suPath;
}
public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
/* 检查访问权限 */
if (!device.canRead() || !device.canWrite()) {
try {
/* 没有读/写权限,尝试对文件进行提权 */
Process su = Runtime.getRuntime().exec(mSuPath);
String cmd = "chmod 777 " + 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.i(TAG, "open() return null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
/**
* 打开串口
*
* @param path 设备路径
* @param baudrate 波特率
* @param flags
* @return FileDescriptor
*/
private native static FileDescriptor open(String path, int baudrate, int flags);
/**
* 关闭串口
*/
public native void close();
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
}
(2)SerialPortUtil
package com.aill.androidserialport;
import android.util.Log;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class SerialPortUtil {
private static SerialPort serialPort = null;
private static InputStream inputStream = null;
private static OutputStream outputStream = null;
private static ReceiveThread mReceiveThread = null;
private static boolean isStart = false;
/**
* 打开串口,接收数据
* 通过串口,接收单片机发送来的数据
*/
public static void openSerialPort(String tty, int baudRate, int flags) {
try {
serialPort = new SerialPort(new File(tty), baudRate, flags);
//调用对象SerialPort方法,获取串口中"读和写"的数据流
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
isStart = true;
} catch (IOException e) {
e.printStackTrace();
}
getSerialPort();
}
/**
* 关闭串口
* 关闭串口中的输入输出流
*/
public static void closeSerialPort() {
Log.i("close", "关闭串口");
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
isStart = false;
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送数据
* 通过串口,发送数据到单片机
*
* @param data 要发送的数据
*/
public void sendSerialPort(String data) {
try {
byte[] sendData = ByteUtil.hexStringToByteArray(data);
outputStream.write(sendData);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void getSerialPort() {
if (mReceiveThread == null) {
mReceiveThread = new ReceiveThread();
}
mReceiveThread.start();
}
/**
* 接收串口数据的线程
*/
private static class ReceiveThread extends Thread {
@Override
public void run() {
super.run();
while (isStart) {
if (inputStream == null) {
return;
}
try {
byte[] readData = new byte[inputStream.available()];
int size = inputStream.read(readData);
if (size > 0) {
String readString = ByteUtil.hexBytesToString(readData);
EventBus.getDefault().post(readString);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(3)ByteUtil
package com.aill.androidserialport;
public class ByteUtil {
private static char[] hexArray = "0123456789ABCDEF".toCharArray();
/**
* 字符串转字节数组
* @param s 字符串
* @return 数组
*/
public static byte[] hexStringToByteArray(String s) {
if (s.length() < 2) {
s = "0" + s;
}
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
/**
* 字节数组转字符串
* @param hexBytes 数组
* @return 字符串
*/
public static String hexBytesToString(byte[] hexBytes) {
char[] hexChars = new char[hexBytes.length * 2];
for (int j = 0; j < hexBytes.length; j++) {
int v = hexBytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
/**
* 16进制字符串转int
* @param hexString 字符串
* @return int
*/
public static int hexStringToInt(String hexString) {
return Integer.parseInt(hexString, 16);
}
/**
* 16进制字符串转byte数组
* @param hexString the hex string
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* char转换位byte
* @param c char
* @return byte
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
}
3 串口测试。(这里有两点很重要:物理设备的串口 ,物理设备的波特率)
物理设备的串口:这里是串口3,所以第一个参数为 "/dev/ttyS3" ,物理设备的波特率:xxxx , 这些信息需要物理设备的厂商确认
(1)在 MainActivity 的 onCreate()下
SerialPortUtil.openSerialPort("/dev/ttyS3" , xxxx , 0);
EventBus.getDefault().register(this);
(2)在 MainActivity 下
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void receiveStudentEventBus(String card){
System.out.println(card);
}