安卓串口通信文章目录
1.添加依赖环境
// 串口
implementation 'com.licheedev:android-serialport:2.1.2'
implementation 'com.github.licheedev.CommonSize:common_size_w1080_n1920:1.3.1'
//rx
implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
// eventbus
implementation 'org.greenrobot:eventbus:3.2.0'
// 选文件的
implementation 'ru.bartwell:exfilepicker:2.1'
// 硬件操作工具类
implementation 'com.licheedev:hardwareutils:1.0.0'
implementation 'com.licheedev:logplus:1.0.0'
需要将项目整体升级为AndroidX 环境以使用android 官方自带的串口操作接口,不升级则会报错找不到类,如果不能将项目升级为Android X 环境 可以使用文档底部提供的Demo,里面有底层串口读写的so包,包括so对应的c、c++源码,可以根据自己的主板架构自行编译
2.实现Base接口 这里提供给Fragment
public class SerialPortFragment extends GlobalFragment {
private Device mDevice;
protected void initSerialPortDevice(){
mDevice = new Device("/dev/ttyS1","115200");
}
protected void switchSerialPort(boolean mOpened) {
if (mOpened) {
SerialPortManager.instance().close();
mOpened = false;
} else {
mOpened = SerialPortManager.instance().open(mDevice) != null;
if (mOpened) {
Log.i(getClass().getSimpleName(), "switchSerialPort: 成功打开串口");
} else {
Log.i(getClass().getSimpleName(), "switchSerialPort: 打开串口失败");
}
}
}
protected void sendData(String cmd) {
SerialPortManager.instance().sendCommand(cmd);
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(IMessage message) {
// 收到时间,刷新界面
refreshCurrentTemp(message.getMessage());
}
private void refreshCurrentTemp(String message) {
Log.i(getClass().getSimpleName(), "refreshCurrentTemp: "+message);
}
}
3.Device Bean 类
public class Device {
private String path;
private String baudrate;
public Device() {
}
public Device(String path, String baudrate) {
this.path = path;
this.baudrate = baudrate;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getBaudrate() {
return baudrate;
}
public void setBaudrate(String baudrate) {
this.baudrate = baudrate;
}
@Override
public String toString() {
return "Device{" + "path='" + path + '\'' + ", baudrate='" + baudrate + '\'' + '}';
}
}
4.基于单例的全局串口管理器
public class SerialPortManager {
private static final String TAG = "SerialPortManager";
private SerialReadThread mReadThread;
private OutputStream mOutputStream;
private HandlerThread mWriteThread;
private Scheduler mSendScheduler;
private static class InstanceHolder {
public static SerialPortManager sManager = new SerialPortManager();
}
public static SerialPortManager instance() {
return InstanceHolder.sManager;
}
private SerialPort mSerialPort;
private SerialPortManager() {
}
/**
* 打开串口
*
* @param device
* @return
*/
public SerialPort open(Device device) {
return open(device.getPath(), device.getBaudrate());
}
/**
* 打开串口
*
* @param devicePath
* @param baudrateString
* @return
*/
public SerialPort open(String devicePath, String baudrateString) {
if (mSerialPort != null) {
close();
}
try {
File device = new File(devicePath);
int baurate = Integer.parseInt(baudrateString);
mSerialPort = new SerialPort(device, baurate);
mReadThread = new SerialReadThread(mSerialPort.getInputStream());
mReadThread.start();
mOutputStream = mSerialPort.getOutputStream();
mWriteThread = new HandlerThread("write-thread");
mWriteThread.start();
mSendScheduler = AndroidSchedulers.from(mWriteThread.getLooper());
return mSerialPort;
} catch (Throwable tr) {
LogPlus.e(TAG, "打开串口失败", tr);
close();
return null;
}
}
/**
* 关闭串口
*/
public void close() {
if (mReadThread != null) {
mReadThread.close();
}
if (mOutputStream != null) {
try {
mOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mWriteThread != null) {
mWriteThread.quit();
}
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
}
}
/**
* 发送数据
*
* @param datas
* @return
*/
private void sendData(byte[] datas) throws Exception {
mOutputStream.write(datas);
}
/**
* (rx包裹)发送数据
*
* @param datas
* @return
*/
private Observable<Object> rxSendData(final byte[] datas) {
return Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
try {
sendData(datas);
emitter.onNext(new Object());
} catch (Exception e) {
LogPlus.e("发送:" + ByteUtil.bytes2HexStr(datas) + " 失败", e);
if (!emitter.isDisposed()) {
emitter.onError(e);
return;
}
}
emitter.onComplete();
}
});
}
/**
* 发送命令包
*/
public void sendCommand(final String command) {
LogPlus.i("发送命令:" + command);
byte[] bytes = ByteUtil.hexStr2bytes(command);
rxSendData(bytes).subscribeOn(mSendScheduler).subscribe(new Observer<Object>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Object o) {
LogManager.instance().post(new SendMessage(command));
}
@Override
public void onError(Throwable e) {
LogPlus.e("发送失败", e);
}
@Override
public void onComplete() {
}
});
}
}
5.读串口线程
public class SerialReadThread extends Thread {
private static final String TAG = "SerialReadThread";
private BufferedInputStream mInputStream;
public SerialReadThread(InputStream is) {
mInputStream = new BufferedInputStream(is);
}
@Override
public void run() {
byte[] received = new byte[1024];
int size;
LogPlus.e("开始读线程");
while (true) {
if (Thread.currentThread().isInterrupted()) {
break;
}
try {
int available = mInputStream.available();
if (available > 0) {
size = mInputStream.read(received);
if (size > 0) {
onDataReceive(received, size);
}
} else {
// 暂停一点时间,免得一直循环造成CPU占用率过高
SystemClock.sleep(1);
}
} catch (IOException e) {
LogPlus.e("读取数据失败", e);
}
//Thread.yield();
}
LogPlus.e("结束读进程");
}
/**
* 处理获取到的数据
*
* @param received
* @param size
*/
private void onDataReceive(byte[] received, int size) {
// TODO: 2018/3/22 解决粘包、分包等
String hexStr = ByteUtil.bytes2HexStr(received, 0, size);
LogManager.instance().post(new RecvMessage(hexStr));
}
/**
* 停止读线程
*/
public void close() {
try {
mInputStream.close();
} catch (IOException e) {
LogPlus.e("异常", e);
} finally {
super.interrupt();
}
}
}
6.实现用于通信的消息类
6.1 日志消息数据接口
public interface IMessage {
/**
* 消息文本
*
* @return
*/
String getMessage();
/**
* 是否发送的消息
*
* @return
*/
boolean isToSend();
}
6.2 log管理类
public class LogManager {
public final List<IMessage> messages;
private boolean mAutoEnd = true;
public LogManager() {
messages = new ArrayList<>();
}
private static class InstanceHolder {
public static LogManager sManager = new LogManager();
}
public static LogManager instance() {
return InstanceHolder.sManager;
}
public void add(IMessage message) {
messages.add(message);
}
public void post(IMessage message) {
EventBus.getDefault().post(message);
}
public void clear() {
messages.clear();
}
public boolean isAutoEnd() {
return mAutoEnd;
}
public void setAutoEnd(boolean autoEnd) {
mAutoEnd = autoEnd;
}
public void changAutoEnd() {
mAutoEnd = !mAutoEnd;
}
}
6.3 收到的日志
public class RecvMessage implements IMessage {
private String command;
private String message;
public static final SimpleDateFormat DEFAULT_FORMAT =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public RecvMessage(String command) {
this.command = command;
this.message = currentTime() + " 收到命令:" + command;
}
@Override
public String getMessage() {
return message;
}
@Override
public boolean isToSend() {
return false;
}
public static String currentTime() {
Date date = new Date();
return DEFAULT_FORMAT.format(date);
}
}
6.4 发送的日志
public class SendMessage implements IMessage {
private String command;
private String message;
public SendMessage(String command) {
this.command = command;
this.message = currentTime() + " 发送命令:" + command;
}
@Override
public String getMessage() {
return message;
}
@Override
public boolean isToSend() {
return true;
}
}
使用时集成基础接口调用即可,获取数据采取重写接口方法
7.Demo下载
Demo | 适用场景 |
---|---|
Demo 1 点击下载已加速 | 基于JNI读写底层,无须升级Android x 以及API 29 |
Demo 2 点击下载已加速 | 基于android 标准接口,支持较低版本的Gradle |