注意:单独监听就不能使用baseConnectProvider.read接口
15 | * @author Kelly 16 | * @version 1.0.0 17 | * @filename ReadThread 18 | * @time 2024/3/4 18:23 19 | * @copyright(C) 2024 song 20 | */ 21 | public class ReadThread extends Thread { 22 | private BaseIoConnectProvider connectProvider; 23 | private boolean running; 24 | 25 | public ReadThread(BaseIoConnectProvider connectProvider) { 26 | this.connectProvider = connectProvider; 27 | } 28 | 29 | @Override 30 | public void run() { 31 | running = true; 32 | LogUtils.i("Start read thread."); 33 | InputStream inputStream = connectProvider.getInputStream(); 34 | while (running) { 35 | if (inputStream == null) { 36 | return; 37 | } 38 | try { 39 | byte[] readData = new byte[128]; 40 | int size = inputStream.read(readData); 41 | if (size > 0) { 42 | synchronized (this){ 43 | byte[] tempBuffer = new byte[size]; 44 | System.arraycopy(readData, 0, tempBuffer, 0, tempBuffer.length); 45 | MessageEventUtils.sendLog("收到服务端数据:" + new String(tempBuffer)); 46 | } 47 | } 48 | } catch (Exception e) { 49 | LogUtils.e("Data read exception", e); 50 | } 51 | } 52 | LogUtils.w("The read thread stop."); 53 | } 54 | 55 | public void close(){ 56 | running = false; 57 | try { 58 | connectProvider.getInputStream().close(); 59 | } catch (IOException e) { 60 | 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjl/deviceconnector/test/util/MessageEventUtils.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.test.util; 2 | 3 | import com.sjl.deviceconnector.test.entity.MessageEvent; 4 | 5 | import org.greenrobot.eventbus.EventBus; 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author Kelly 11 | * @version 1.0.0 12 | * @filename MessageEventUtils 13 | * @time 2022/7/26 20:29 14 | * @copyright(C) 2022 song 15 | */ 16 | public class MessageEventUtils { 17 | 18 | public static void sendLog(String log) { 19 | MessageEvent.Builderindicate:译为“指示”,它是服务器给客户端发送数据的方式。它在使用上比notify多一个应答的步骤。
6 | * 7 | * @author Kelly 8 | * @version 1.0.0 9 | * @filename IndicateRequest 10 | * @time 2023/3/4 14:32 11 | * @copyright(C) 2023 song 12 | */ 13 | public class IndicateRequest extends BluetoothLeRequest { 14 | /** 15 | * true开启通知,false关闭 16 | */ 17 | private boolean enable; 18 | 19 | public boolean isEnable() { 20 | return enable; 21 | } 22 | 23 | public void setEnable(boolean enable) { 24 | this.enable = enable; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/device/bluetooth/ble/request/MtuRequest.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.device.bluetooth.ble.request; 2 | 3 | /** 4 | * mtu设置请求 5 | * 6 | * @author Kelly 7 | * @version 1.0.0 8 | * @filename MtuRequest 9 | * @time 2023/3/4 14:33 10 | * @copyright(C) 2023 song 11 | */ 12 | public class MtuRequest extends BluetoothLeRequest { 13 | private int mtu; 14 | 15 | public int getMtu() { 16 | return mtu; 17 | } 18 | 19 | /** 20 | * 21 | * 设置MTU,不同的蓝牙版本最大MTU不同,蓝牙4.2的最大MTU=247Byte,蓝牙5.0的最大MTU=512Byte。 22 | *蓝牙一般默认支持的MTU长度是23个字节,一有效的最大MTU还需要减去协议Byte、Opcode和Handler,实际传输数据就是20字节。
23 | * 24 | *通过gatt.requestMtu(mtu)。会触发onMtuChanged回调。这里mtu 的范围在23 ~ 512之间,目前市面上Android版本高的手机(支持蓝牙5.0)基本上都是247。也就是设置mtu = 512,回调中的mtu可能还是247,真本事因为手机做了限制
25 | * 26 | * 27 | * @param mtu 23字节~512字节 28 | */ 29 | public void setMtu(int mtu) { 30 | this.mtu = mtu; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/device/bluetooth/ble/request/NotifyRequest.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.device.bluetooth.ble.request; 2 | 3 | /** 4 | * 特性值通知请求(单向) 5 | * 6 | * @author Kelly 7 | * @version 1.0.0 8 | * @filename NotifyRequest 9 | * @time 2023/3/4 14:33 10 | * @copyright(C) 2023 song 11 | */ 12 | public class NotifyRequest extends BluetoothLeRequest { 13 | /** 14 | * true开启通知,false关闭 15 | */ 16 | private boolean enable; 17 | 18 | public boolean isEnable() { 19 | return enable; 20 | } 21 | 22 | public void setEnable(boolean enable) { 23 | this.enable = enable; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/device/bluetooth/ble/request/RemoteRssiRequest.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.device.bluetooth.ble.request; 2 | 3 | /** 4 | * 远程Rssi请求 5 | * 6 | * @author Kelly 7 | * @version 1.0.0 8 | * @filename RemoteRssiRequest 9 | * @time 2023/3/4 14:33 10 | * @copyright(C) 2023 song 11 | */ 12 | public class RemoteRssiRequest extends BluetoothLeRequest { 13 | } 14 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/device/bluetooth/ble/response/BluetoothLeResponse.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.device.bluetooth.ble.response; 2 | 3 | import java.util.Arrays; 4 | import java.util.UUID; 5 | 6 | /** 7 | * Ble响应 8 | * 9 | * @author Kelly 10 | * @version 1.0.0 11 | * @filename BluetoothLeResponse 12 | * @time 2023/3/4 15:18 13 | * @copyright(C) 2023 song 14 | */ 15 | public class BluetoothLeResponse { 16 | private int code; 17 | private byte[] data; 18 | private int rssi = -1; 19 | private int mtu = -1; 20 | 21 | public int getCode() { 22 | return code; 23 | } 24 | 25 | public void setCode(int code) { 26 | this.code = code; 27 | } 28 | 29 | public byte[] getData() { 30 | return data; 31 | } 32 | 33 | public void setData(byte[] data) { 34 | this.data = data; 35 | } 36 | 37 | public int getRssi() { 38 | return rssi; 39 | } 40 | 41 | public void setRssi(int rssi) { 42 | this.rssi = rssi; 43 | } 44 | 45 | public int getMtu() { 46 | return mtu; 47 | } 48 | 49 | public void setMtu(int mtu) { 50 | this.mtu = mtu; 51 | } 52 | 53 | public void copy(BluetoothLeResponse buffer) { 54 | this.code = buffer.code; 55 | this.data = buffer.data; 56 | this.rssi = buffer.rssi; 57 | this.mtu = buffer.mtu; 58 | 59 | } 60 | 61 | public void reset() { 62 | this.code = 0; 63 | this.data = null; 64 | this.rssi = -1; 65 | this.mtu = -1; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "{" + 71 | "code=" + code + 72 | ", data=" + Arrays.toString(data) + 73 | ", rssi=" + rssi + 74 | ", mtu=" + mtu + 75 | '}'; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/device/bluetooth/scanner/AbstractBluetoothScanner.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.device.bluetooth.scanner; 2 | 3 | import com.sjl.deviceconnector.entity.BluetoothScanResult; 4 | import com.sjl.deviceconnector.listener.BluetoothScanListener; 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author Kelly 10 | * @version 1.0.0 11 | * @filename AbstractBluetoothScanner 12 | * @time 2023/3/3 14:58 13 | * @copyright(C) 2023 song 14 | */ 15 | public abstract class AbstractBluetoothScanner implements BluetoothScanner{ 16 | 17 | protected BluetoothScanListener bluetoothScanListener; 18 | protected String address; 19 | 20 | 21 | 22 | /** 23 | * 扫描监听 24 | * @param bluetoothScanListener 25 | */ 26 | public void setBluetoothScanListener(BluetoothScanListener bluetoothScanListener) { 27 | this.bluetoothScanListener = bluetoothScanListener; 28 | } 29 | 30 | public void setAddress(String address) { 31 | this.address = address; 32 | } 33 | 34 | 35 | public void removeListener(){ 36 | this.address = null; 37 | this.bluetoothScanListener = null; 38 | } 39 | 40 | protected void notifyDeviceFounded(BluetoothScanResult device) { 41 | if (bluetoothScanListener != null) { 42 | bluetoothScanListener.onDeviceFound(device); 43 | } 44 | } 45 | 46 | public void notifyScanFinish() { 47 | if (bluetoothScanListener != null) { 48 | bluetoothScanListener.onScanFinish(); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/device/bluetooth/scanner/BluetoothClassicScanner.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.device.bluetooth.scanner; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.text.TextUtils; 10 | 11 | import com.sjl.deviceconnector.DeviceContext; 12 | import com.sjl.deviceconnector.entity.BluetoothScanResult; 13 | import com.sjl.deviceconnector.listener.ReceiverObservable; 14 | import com.sjl.deviceconnector.util.BluetoothUtils; 15 | import com.sjl.deviceconnector.util.LogUtils; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * 经典蓝牙扫描 21 | * 22 | * @author Kelly 23 | * @version 1.0.0 24 | * @filename BluetoothClassicScanner 25 | * @time 2023/3/3 14:47 26 | * @copyright(C) 2023 song 27 | */ 28 | public class BluetoothClassicScanner extends AbstractBluetoothScanner implements ReceiverObservable { 29 | private BroadcastReceiver mBroadcastReceiver; 30 | 31 | @Override 32 | public void startScan() { 33 | ListAndroid4.3开始,开始支持BLE功能,但只支持Central Mode(中心模式,可连接外围设备)
24 | *Android5.0开始,开始支持Peripheral Mode(外围模式,可被中心设备连接)
25 | * 26 | * @author Kelly 27 | * @version 1.0.0 28 | * @filename BluetoothLowEnergyScanner 29 | * @time 2023/3/3 14:49 30 | * @copyright(C) 2023 song 31 | */ 32 | public class BluetoothLowEnergyScanner extends AbstractBluetoothScanner { 33 | 34 | 35 | @Override 36 | public void startScan() { 37 | //android 5.0后 38 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 39 | BluetoothLeScanner bluetoothLeScanner = BluetoothUtils.getBluetoothLeScanner(); 40 | //创建ScanSettings的build对象用于设置参数 41 | /*ScanSettings.Builder builder = new ScanSettings.Builder() 42 | //设置低功耗模式 43 | .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); 44 | //android 6.0添加设置回调类型、匹配模式等 45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 46 | //定义回调类型 47 | builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES); 48 | //设置蓝牙LE扫描滤波器硬件匹配的匹配模式 49 | builder.setMatchMode(ScanSettings.MATCH_MODE_STICKY); 50 | } 51 | 52 | //芯片组支持批处理芯片上的扫描 53 | if (BluetoothUtils.getBluetoothAdapter().isOffloadedScanBatchingSupported()) { 54 | //不开启批处理扫描模式,即不回调onBatchScanResults 55 | builder.setReportDelay(0L); 56 | } 57 | ScanSettings scanSettings = builder.build();*/ 58 | bluetoothLeScanner.startScan(mScanCallback); 59 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 60 | BluetoothUtils.getBluetoothAdapter().startLeScan(mLeScanCallback); 61 | } else { 62 | throw new RuntimeException("系统版本过低,不支持ble,当前系统版本为:" + Build.VERSION.SDK_INT); 63 | } 64 | 65 | } 66 | 67 | @Override 68 | public void stopScan() { 69 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 70 | BluetoothLeScanner bluetoothLeScanner = BluetoothUtils.getBluetoothLeScanner(); 71 | if (bluetoothLeScanner != null){ 72 | bluetoothLeScanner.stopScan(mScanCallback); 73 | } 74 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 75 | BluetoothAdapter bluetoothAdapter = BluetoothUtils.getBluetoothAdapter(); 76 | if (bluetoothAdapter != null){ 77 | bluetoothAdapter.stopLeScan(mLeScanCallback); 78 | } 79 | 80 | } 81 | notifyScanFinish(); 82 | removeListener(); 83 | } 84 | 85 | /** 86 | * 5.0以上扫描回调 87 | */ 88 | @SuppressLint("NewApi") 89 | private final ScanCallback mScanCallback = new ScanCallback() { 90 | 91 | @Override 92 | public void onScanResult(int callbackType, ScanResult result) { 93 | BluetoothScanResult bluetoothScanResult = new BluetoothScanResult(result.getDevice(), result.getRssi(), result.getScanRecord()); 94 | bluetoothScanResult.setScanRecordBytes(result.getScanRecord().getBytes()); 95 | 96 | notifyDeviceFounded(bluetoothScanResult); 97 | 98 | if (!TextUtils.isEmpty(address) && address.equals(bluetoothScanResult.getAddress())) { 99 | stopScan(); 100 | } 101 | } 102 | 103 | @Override 104 | public void onBatchScanResults(List广播包中包含若干个广播数据单元,广播数据单元也称为 AD Structure。
9 | *广播数据单元 = 长度值Length + AD type + AD Data。
10 | *长度值Length只占一个字节,并且位于广播数据单元的第一个字节;AD type也是一个字节;AD Data长度为Length-1
11 | * 12 | * @author Kelly 13 | * @version 1.0.0 14 | * @filename AdStructure 15 | * @time 2023/3/12 20:28 16 | * @copyright(C) 2023 song 17 | */ 18 | public class AdStructure { 19 | /** 20 | * 广播中声明的长度 21 | */ 22 | public int length; 23 | 24 | /** 25 | * 广播中声明的type 26 | *type = 0x01 表示设备LE物理连接。
27 | *type = 0x09 表示设备的全名(转为ASCII码)
> 28 | *type = 0x03 表示完整的16bit UUID。
29 | *type = 0xFF 表示厂商数据。前两个字节表示厂商ID,后面的为厂商数据,具体由用户自行定义。
30 | *type = 0x16 结合Type = 0x03看,表示16 bit UUID对应的数据,具体由用户自行定义。
31 | * 32 | */ 33 | public int type; 34 | 35 | /** 36 | * 广播中的数据部分 37 | */ 38 | public byte[] data; 39 | 40 | 41 | @Override 42 | public String toString() { 43 | return "AdStructure{" + 44 | "length=0x" +String.format("%02X", length & 0xff) +"->"+length+ 45 | ", type=0x" + String.format("%02X", type & 0xff) +"->"+type+ 46 | ", data=0x" + ByteUtils.byteArrToHexString(data) + 47 | '}'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/entity/BluetoothScanResult.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.entity; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.bluetooth.le.ScanRecord; 5 | import android.os.Parcel; 6 | import android.os.Parcelable; 7 | import android.text.TextUtils; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * TODO 13 | * 14 | * @author Kelly 15 | * @version 1.0.0 16 | * @filename BluetoothScanResult 17 | * @time 2023/3/3 14:52 18 | * @copyright(C) 2023 song 19 | */ 20 | public class BluetoothScanResult { 21 | 22 | private BluetoothDevice device; 23 | 24 | /** 25 | * 可理解成设备的信号值。该数值是一个负数,越大则信号越强。 26 | */ 27 | private int rssi; 28 | 29 | /** 30 | * 远程设备提供的广播数据的内容。 31 | */ 32 | private byte[] scanRecordBytes; 33 | /** 34 | * 远程设备提供的广播数据的内容。 35 | */ 36 | private ScanRecord scanRecord; 37 | 38 | 39 | 40 | public BluetoothScanResult(BluetoothDevice device, int rssi, byte[] scanRecordBytes) { 41 | this.device = device; 42 | this.rssi = rssi; 43 | this.scanRecordBytes = scanRecordBytes; 44 | } 45 | 46 | public BluetoothScanResult(BluetoothDevice device, int rssi, ScanRecord scanRecord) { 47 | this.device = device; 48 | this.rssi = rssi; 49 | this.scanRecord = scanRecord; 50 | } 51 | 52 | public BluetoothDevice getDevice() { 53 | return device; 54 | } 55 | 56 | public void setDevice(BluetoothDevice device) { 57 | this.device = device; 58 | } 59 | 60 | public String getName() { 61 | String name = getDevice().getName(); 62 | return TextUtils.isEmpty(name) || TextUtils.equals("null", name) ? "Unknown" : name; 63 | } 64 | 65 | public String getAddress() { 66 | String address = getDevice().getAddress(); 67 | return TextUtils.isEmpty(address) ? "--" : address; 68 | } 69 | 70 | 71 | public int getBondState() { 72 | return getDevice().getBondState(); 73 | } 74 | 75 | public int getRssi() { 76 | return rssi; 77 | } 78 | 79 | public void setRssi(int rssi) { 80 | this.rssi = rssi; 81 | } 82 | 83 | public byte[] getScanRecordBytes() { 84 | return scanRecordBytes; 85 | } 86 | 87 | public void setScanRecordBytes(byte[] scanRecordBytes) { 88 | this.scanRecordBytes = scanRecordBytes; 89 | } 90 | 91 | public ScanRecord getScanRecord() { 92 | return scanRecord; 93 | } 94 | 95 | public void setScanRecord(ScanRecord scanRecord) { 96 | this.scanRecord = scanRecord; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/entity/SerialPortConfig.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.entity; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * 串口配置 7 | * 8 | * @author Kelly 9 | * @version 1.0.0 10 | * @filename SerialPortConfig 11 | * @time 2020/11/9 15:59 12 | * @copyright(C) 2020 song 13 | */ 14 | public class SerialPortConfig { 15 | 16 | private File device; 17 | private int baudRate; 18 | private int dataBits; 19 | private int parity; 20 | private int stopBits; 21 | private int flags; 22 | 23 | public SerialPortConfig(Builder builder) { 24 | this.device = builder.device; 25 | this.baudRate = builder.baudRate; 26 | this.dataBits = builder.dataBits; 27 | this.parity = builder.parity; 28 | this.stopBits = builder.stopBits; 29 | this.flags = builder.flags; 30 | } 31 | 32 | public File getDevice() { 33 | return device; 34 | } 35 | 36 | public int getBaudRate() { 37 | return baudRate; 38 | } 39 | 40 | public int getDataBits() { 41 | return dataBits; 42 | } 43 | 44 | public int getParity() { 45 | return parity; 46 | } 47 | 48 | public int getStopBits() { 49 | return stopBits; 50 | } 51 | 52 | public int getFlags() { 53 | return flags; 54 | } 55 | 56 | public static Builder newBuilder(int baudrate) { 57 | return new Builder(baudrate); 58 | } 59 | public static Builder newBuilder(File device, int baudrate) { 60 | return new Builder(device, baudrate); 61 | } 62 | 63 | public static Builder newBuilder(String devicePath, int baudrate) { 64 | return new Builder(devicePath, baudrate); 65 | } 66 | 67 | public final static class Builder { 68 | 69 | private File device; 70 | private int baudRate; 71 | private int dataBits = 8; 72 | private int parity = 0; 73 | private int stopBits = 1; 74 | private int flags = 0; 75 | 76 | private Builder(int baudRate) { 77 | this.baudRate = baudRate; 78 | } 79 | 80 | private Builder(File device, int baudRate) { 81 | this.device = device; 82 | this.baudRate = baudRate; 83 | } 84 | 85 | private Builder(String devicePath, int baudrate) { 86 | this(new File(devicePath), baudrate); 87 | } 88 | 89 | /** 90 | * 波特率 91 | * 92 | * @param baudRate 93 | * @return 94 | */ 95 | public Builder baudRate(int baudRate) { 96 | this.baudRate = baudRate; 97 | return this; 98 | } 99 | 100 | /** 101 | * 数据位 102 | * 103 | * @param dataBits 默认8,可选值为5~8 104 | * @return 105 | */ 106 | public Builder dataBits(int dataBits) { 107 | this.dataBits = dataBits; 108 | return this; 109 | } 110 | 111 | /** 112 | * 校验位 113 | * 114 | * @param parity 0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) 115 | * @return 116 | */ 117 | public Builder parity(int parity) { 118 | this.parity = parity; 119 | return this; 120 | } 121 | 122 | /** 123 | * 停止位 124 | * 125 | * @param stopBits 默认1;1:1位停止位;2:2位停止位 126 | * @return 127 | */ 128 | public Builder stopBits(int stopBits) { 129 | this.stopBits = stopBits; 130 | return this; 131 | } 132 | 133 | /** 134 | * 标志 135 | * 136 | * @param flags 默认0 137 | * @return 138 | */ 139 | public Builder flags(int flags) { 140 | this.flags = flags; 141 | return this; 142 | } 143 | 144 | 145 | public SerialPortConfig build(){ 146 | return new SerialPortConfig(this); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/exception/ProviderTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.exception; 2 | 3 | /** 4 | * 连接提供者超时异常处理 5 | * 6 | * @author Kelly 7 | * @version 1.0.0 8 | * @filename ProviderTimeoutException 9 | * @time 2022/7/23 11:27 10 | * @copyright(C) 2022 song 11 | */ 12 | public class ProviderTimeoutException extends RuntimeException{ 13 | 14 | public ProviderTimeoutException() { 15 | } 16 | 17 | public ProviderTimeoutException(String message) { 18 | super(message); 19 | } 20 | 21 | public ProviderTimeoutException(String message, Throwable cause) { 22 | super(message, cause); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/listener/BluetoothScanListener.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.listener; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | 5 | import com.sjl.deviceconnector.entity.BluetoothScanResult; 6 | 7 | /** 8 | * 蓝牙扫描监听 9 | * 10 | * @author Kelly 11 | * @version 1.0.0 12 | * @filename BluetoothScanListener 13 | * @time 2022/8/4 16:26 14 | * @copyright(C) 2022 song 15 | */ 16 | public interface BluetoothScanListener { 17 | void onDeviceFound(BluetoothScanResult bluetoothScanResult); 18 | 19 | void onScanFinish(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/listener/ConnectedListener.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.listener; 2 | 3 | 4 | /** 5 | * 设备连接状态监听 6 | * 7 | * @author Kelly 8 | * @version 1.0.0 9 | * @filename ConnectedListener 10 | * @time 2022/8/4 16:14 11 | * @copyright(C) 2022 song 12 | */ 13 | public interface ConnectedListener连接流程:打开蓝牙开关->搜索蓝牙->配对蓝牙->连接蓝牙->读写数据->断开蓝牙->关闭蓝牙开关
15 | * @author Kelly 16 | * @version 1.0.0 17 | * @filename BluetoothConnectProvider.java 18 | * @time 2018/4/13 8:43 19 | * @copyright(C) 2018 song 20 | */ 21 | public class BluetoothConnectProvider extends BaseIoConnectProvider { 22 | /** 23 | * SPP服务UUID号 24 | */ 25 | public final static String BLUETOOTH_UUID = "00001101-0000-1000-8000-00805F9B34FB"; 26 | private final BluetoothDevice mBluetoothDevice; 27 | private BluetoothSocket mBluetoothSocket; 28 | private String uuid = BLUETOOTH_UUID; 29 | 30 | 31 | /** 32 | * 初始化一个蓝牙提供者 33 | * 34 | * @param address 蓝牙mac地址 35 | */ 36 | public BluetoothConnectProvider(String address) { 37 | this(address, BLUETOOTH_UUID); 38 | } 39 | 40 | /** 41 | * 初始化一个蓝牙提供者 42 | * 43 | * @param bluetoothDevice 蓝牙设备 44 | */ 45 | public BluetoothConnectProvider(BluetoothDevice bluetoothDevice) { 46 | this(bluetoothDevice, BLUETOOTH_UUID); 47 | } 48 | 49 | /** 50 | * 根据指定蓝牙服务UUID,初始化一个蓝牙提供者 51 | * 52 | * @param address 53 | * @param uuid 54 | */ 55 | public BluetoothConnectProvider(String address, String uuid) { 56 | BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter(); 57 | if (defaultAdapter == null) { 58 | throw new NullPointerException("设备不支持蓝牙"); 59 | } 60 | this.mBluetoothDevice = defaultAdapter.getRemoteDevice(address); 61 | this.uuid = uuid; 62 | } 63 | 64 | /** 65 | * 根据指定蓝牙服务UUID,初始化一个蓝牙提供者 66 | * 67 | * @param bluetoothDevice 68 | * @param uuid 69 | */ 70 | public BluetoothConnectProvider(BluetoothDevice bluetoothDevice, String uuid) { 71 | this.mBluetoothDevice = bluetoothDevice; 72 | this.uuid = uuid; 73 | } 74 | 75 | 76 | 77 | @Override 78 | public int open() { 79 | int state = getState(); 80 | if (state == ErrorCode.ERROR_OK) { 81 | return state; 82 | } 83 | try { 84 | mBluetoothSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid)); 85 | mBluetoothSocket.connect(); 86 | mConnectState = true; 87 | mInputStream = mBluetoothSocket.getInputStream(); 88 | mOutputStream = mBluetoothSocket.getOutputStream(); 89 | return ErrorCode.ERROR_OK; 90 | } catch (Exception e) { 91 | LogUtils.e("蓝牙连接异常", e); 92 | close(); 93 | return ErrorCode.ERROR_FAIL; 94 | } 95 | } 96 | 97 | @Override 98 | public void close() { 99 | mConnectState = false; 100 | close(getOutputStream()); 101 | close(getInputStream()); 102 | close(mBluetoothSocket); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/provider/IConnectProvider.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.provider; 2 | 3 | /** 4 | * 通用连接提供者接口 5 | * 6 | * @author Kelly 7 | */ 8 | public interface IConnectProvider { 9 | 10 | /** 11 | * 打开连接 12 | * 13 | * @return 0连接成功,-1连接失败 14 | */ 15 | int open(); 16 | 17 | 18 | /** 19 | * 获取连接状态 20 | * 21 | * @return 0连接成功,-1连接失败 22 | */ 23 | int getState(); 24 | 25 | /** 26 | * 写数据(多次写的场景) 27 | * 28 | * @param sendParams 发送命令 29 | * @param timeout 超时时间,单位ms 30 | * @return 0 写成功,-1写失败 31 | */ 32 | int write(byte[] sendParams, int timeout); 33 | 34 | 35 | /** 36 | * 读数据(多次读的场景) 37 | * 38 | * @param buffer 临时缓冲区 39 | * @param timeout 超时时间,单位ms 40 | * @return >0读取数据成功(代表数据长度),-1读取超时,-2数据发送错误,-3数据接收错误,更多错误请看{@link com.sjl.deviceconnector.ErrorCode)} 41 | */ 42 | int read(byte[] buffer, int timeout); 43 | 44 | 45 | /** 46 | * 写和读数据(通用型) 47 | * 48 | * @param sendParams 发送命令 49 | * @param buffer 临时缓冲区 50 | * @param timeout 超时时间,单位ms 51 | * @return >0读取数据成功(代表数据长度),-1读取超时,-2数据发送错误,-3数据接收错误,更多错误请看{@link com.sjl.deviceconnector.ErrorCode)} 52 | */ 53 | int read(byte[] sendParams, byte[] buffer, int timeout); 54 | 55 | 56 | /** 57 | * 关闭连接 58 | */ 59 | void close(); 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/provider/SerialPortConnectProvider.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.provider; 2 | 3 | import android.serialport.SerialPort; 4 | 5 | import com.sjl.deviceconnector.ErrorCode; 6 | import com.sjl.deviceconnector.entity.SerialPortConfig; 7 | import com.sjl.deviceconnector.util.LogUtils; 8 | 9 | /** 10 | * 串口连接提供者 11 | * 12 | * @author Kelly 13 | * @version 1.0.0 14 | * @filename SerialPortConnectProvider 15 | * @time 2020/10/30 11:31 16 | * @copyright(C) 2020 song 17 | */ 18 | public class SerialPortConnectProvider extends BaseIoConnectProvider { 19 | private final SerialPortConfig serialPortConfig; 20 | private SerialPort mSerialPort; 21 | 22 | /** 23 | * 初始化串口连接提供者 24 | * 25 | * @param serialPortConfig 串口配置 26 | */ 27 | public SerialPortConnectProvider(SerialPortConfig serialPortConfig) { 28 | this.serialPortConfig = serialPortConfig; 29 | } 30 | 31 | 32 | @Override 33 | public int open() { 34 | int state = getState(); 35 | if (state == ErrorCode.ERROR_OK) { 36 | return state; 37 | } 38 | try { 39 | mSerialPort = SerialPort.newBuilder(serialPortConfig.getDevice(), serialPortConfig.getBaudRate()) 40 | .parity(serialPortConfig.getParity()) 41 | .flags(serialPortConfig.getFlags()) 42 | .dataBits(serialPortConfig.getDataBits()) 43 | .stopBits(serialPortConfig.getStopBits()).build(); 44 | mOutputStream = mSerialPort.getOutputStream(); 45 | mInputStream = mSerialPort.getInputStream(); 46 | mConnectState = true; 47 | } catch (Exception e) { 48 | LogUtils.e("打开串口失败", e); 49 | close(); 50 | return ErrorCode.ERROR_FAIL; 51 | } 52 | return ErrorCode.ERROR_OK; 53 | } 54 | 55 | 56 | @Override 57 | public void close() { 58 | mConnectState = false; 59 | close(getOutputStream()); 60 | close(getInputStream()); 61 | if (mSerialPort != null) { 62 | mSerialPort.close(); 63 | mSerialPort = null; 64 | } 65 | } 66 | 67 | 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/provider/SocketConnectProvider.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.provider; 2 | 3 | import android.os.Build; 4 | 5 | import com.sjl.deviceconnector.ErrorCode; 6 | 7 | import java.net.InetSocketAddress; 8 | import java.net.Socket; 9 | import java.net.SocketAddress; 10 | 11 | /** 12 | * Socket连接提供者,可作为TCP通讯使用 13 | * 14 | * @author Kelly 15 | * @version 1.0.0 16 | * @filename SocketConnectProvider.java 17 | * @time 2018/4/13 8:40 18 | * @copyright(C) 2018 song 19 | */ 20 | public class SocketConnectProvider extends BaseIoConnectProvider { 21 | private Socket mSocket; 22 | private String ip; 23 | private int port; 24 | private int connectTimeout, readTimeout; 25 | 26 | 27 | /** 28 | * 初始化Socket连接提供者 29 | * 30 | * @param ip ip地址 31 | * @param port 端口号 32 | * @param connectTimeout 连接超时时间,毫秒 33 | * @param readTimeout 读取超时时间,毫秒 34 | */ 35 | public SocketConnectProvider(String ip, int port, int connectTimeout, int readTimeout) { 36 | this.ip = ip; 37 | this.port = port; 38 | this.connectTimeout = connectTimeout < 0 ? 10 * 1000 : connectTimeout; 39 | this.readTimeout = readTimeout < 0 ? 10 * 1000 : readTimeout; 40 | } 41 | 42 | 43 | 44 | 45 | @Override 46 | public int open() { 47 | int state = getState(); 48 | if (state == ErrorCode.ERROR_OK) { 49 | return state; 50 | } 51 | mSocket = new Socket(); 52 | SocketAddress address = new InetSocketAddress(ip, port);//socket连接地址 53 | try { 54 | mSocket.connect(address, connectTimeout); 55 | mSocket.setSoTimeout(readTimeout); 56 | mInputStream = mSocket.getInputStream(); 57 | mOutputStream = mSocket.getOutputStream(); 58 | mConnectState = true; 59 | return ErrorCode.ERROR_OK; 60 | } catch (Exception e) { 61 | close(); 62 | return ErrorCode.ERROR_FAIL; 63 | } 64 | 65 | } 66 | 67 | @Override 68 | public void close() { 69 | mConnectState = false; 70 | close(getOutputStream()); 71 | close(getInputStream()); 72 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 73 | close(mSocket); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/provider/UsbComConnectProvider.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.provider; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.hardware.usb.UsbDevice; 6 | import android.hardware.usb.UsbDeviceConnection; 7 | import android.hardware.usb.UsbManager; 8 | 9 | import com.hoho.android.usbserial.driver.CommonUsbSerialPort; 10 | import com.hoho.android.usbserial.driver.UsbSerialDriver; 11 | import com.hoho.android.usbserial.driver.UsbSerialPort; 12 | import com.hoho.android.usbserial.driver.UsbSerialProber; 13 | import com.hoho.android.usbserial.util.SerialInputOutputManager; 14 | import com.sjl.deviceconnector.DeviceContext; 15 | import com.sjl.deviceconnector.ErrorCode; 16 | import com.sjl.deviceconnector.device.usb.UsbHelper; 17 | import com.sjl.deviceconnector.entity.SerialPortConfig; 18 | import com.sjl.deviceconnector.util.LogUtils; 19 | 20 | /** 21 | * Usb Com连接提供者(基于usb-serial-for-android驱动库封装) 22 | * 23 | * @author Kelly 24 | * @version 1.0.0 25 | * @filename UsbComConnectProvider 26 | * @time 2020/10/30 11:31 27 | * @copyright(C) 2020 song 28 | */ 29 | public class UsbComConnectProvider extends BaseConnectProvider { 30 | private SerialInputOutputManager usbIoManager; 31 | private UsbSerialPort usbSerialPort; 32 | private final UsbDevice mUsbDevice; 33 | private final SerialPortConfig serialPortConfig; 34 | 35 | /** 36 | * 初始化Usb Com连接提供者 37 | * 38 | * @param vendorId 产商id 39 | * @param productId 产品id 40 | * @param serialPortConfig 串口配置参数 41 | */ 42 | public UsbComConnectProvider(int vendorId, int productId, SerialPortConfig serialPortConfig) { 43 | this(UsbHelper.getDevice(vendorId, productId), serialPortConfig); 44 | } 45 | 46 | /** 47 | * 初始化Usb Com连接提供者 48 | * 49 | * @param usbDevice 50 | */ 51 | public UsbComConnectProvider(UsbDevice usbDevice, SerialPortConfig serialPortConfig) { 52 | this.mUsbDevice = usbDevice; 53 | this.serialPortConfig = serialPortConfig; 54 | } 55 | 56 | 57 | @SuppressLint("WrongConstant") 58 | @Override 59 | public int open() { 60 | int state = getState(); 61 | if (state == ErrorCode.ERROR_OK) { 62 | return state; 63 | } 64 | if (mUsbDevice == null) { 65 | LogUtils.e("connection failed: device not found"); 66 | return ErrorCode.ERROR_DEVICE_NOT_FIND; 67 | } 68 | UsbManager usbManager = (UsbManager) DeviceContext.getContext().getSystemService(Context.USB_SERVICE); 69 | UsbSerialDriver usbSerialDriver = UsbSerialProber.getDefaultProber().probeDevice(mUsbDevice); 70 | if (usbSerialDriver == null) { 71 | LogUtils.e("connection failed: device driver not found"); 72 | return ErrorCode.ERROR_DEVICE_DRIVER_NOT_FIND; 73 | } 74 | UsbDeviceConnection usbConnection = usbManager.openDevice(usbSerialDriver.getDevice()); 75 | if (usbConnection == null && !usbManager.hasPermission(usbSerialDriver.getDevice())) { 76 | LogUtils.e("connection failed: open failed"); 77 | //触发一次权限申请,正常使用需要在外部申请usb权限 78 | UsbHelper.getInstance().requestPermission(mUsbDevice); 79 | return ErrorCode.ERROR_NO_PERMISSION; 80 | 81 | } 82 | if (!usbManager.hasPermission(usbSerialDriver.getDevice())) { 83 | LogUtils.e("connection failed: permission denied"); 84 | return ErrorCode.ERROR_PERMISSION_FAIL; 85 | } 86 | try { 87 | usbSerialPort = usbSerialDriver.getPorts().get(0); 88 | usbSerialPort.open(usbConnection); 89 | usbSerialPort.setParameters(serialPortConfig.getBaudRate(), serialPortConfig.getDataBits(), serialPortConfig.getStopBits(), serialPortConfig.getParity()); 90 | try { 91 | usbSerialPort.purgeHwBuffers(true, true); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | LogUtils.e("connected"); 96 | mConnectState = true; 97 | return ErrorCode.ERROR_OK; 98 | } catch (Exception e) { 99 | LogUtils.e("connection failed", e); 100 | close(); 101 | } 102 | return ErrorCode.ERROR_FAIL; 103 | } 104 | 105 | 106 | @Override 107 | public synchronized int write(byte[] sendParams, int timeout) { 108 | int state = getState(); 109 | if (state != ErrorCode.ERROR_OK) { 110 | return state; 111 | } 112 | try { 113 | usbSerialPort.write(sendParams, timeout); 114 | return ErrorCode.ERROR_OK; 115 | } catch (Exception e) { 116 | return ErrorCode.ERROR_FAIL; 117 | } 118 | } 119 | 120 | @Override 121 | public synchronized int read(byte[] buffer, int timeout) { 122 | int state = getState(); 123 | if (state != ErrorCode.ERROR_OK) { 124 | return state; 125 | } 126 | try { 127 | int len = usbSerialPort.read(buffer, timeout); 128 | return len; 129 | } catch (Exception e) { 130 | LogUtils.e("read failed", e); 131 | return ErrorCode.ERROR_FAIL; 132 | } 133 | } 134 | 135 | @Override 136 | public synchronized int read(byte[] sendParams, byte[] buffer, int timeout) { 137 | int ret = write(sendParams, timeout); 138 | if (ret == ErrorCode.ERROR_OK) { 139 | return read(buffer, timeout); 140 | } else { 141 | return ret; 142 | } 143 | 144 | } 145 | 146 | @Override 147 | public void close() { 148 | mConnectState = false; 149 | if (usbIoManager != null) { 150 | usbIoManager.stop(); 151 | usbIoManager = null; 152 | } 153 | close(usbSerialPort); 154 | 155 | } 156 | 157 | /** 158 | * 设置发送缓冲区大小 159 | * 160 | * @param bufferSize 大小字节数 161 | */ 162 | public void setWriteBufferSize(int bufferSize) { 163 | if (usbSerialPort != null && usbSerialPort instanceof CommonUsbSerialPort){ 164 | CommonUsbSerialPort commonUsbSerialPort = (CommonUsbSerialPort) usbSerialPort; 165 | commonUsbSerialPort.setWriteBufferSize(bufferSize); 166 | } 167 | } 168 | 169 | public UsbSerialPort getUsbSerialPort() { 170 | return usbSerialPort; 171 | } 172 | 173 | public int getMaxWriteBuffer() { 174 | return usbSerialPort.getWriteEndpoint().getMaxPacketSize(); 175 | } 176 | 177 | public int getMaxReadBuffer() { 178 | return usbSerialPort.getReadEndpoint().getMaxPacketSize(); 179 | } 180 | 181 | 182 | } 183 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/provider/WifiConnectProvider.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.provider; 2 | 3 | 4 | 5 | /** 6 | * Wifi连接提供者 7 | * 8 | * @author Kelly 9 | * @version 1.0.0 10 | * @filename WifiConnectProvider.java 11 | * @time 2018/4/13 8:45 12 | * @copyright(C) 2018 song 13 | */ 14 | public class WifiConnectProvider extends SocketConnectProvider { 15 | 16 | 17 | /** 18 | * 初始化Wifi连接提供者 19 | * 20 | * @param host ip地址 21 | * @param port 端口号 22 | * @param connectTimeout 连接超时时间,毫秒 23 | * @param readTimeout 读取超时时间,毫秒 24 | */ 25 | public WifiConnectProvider(String host, int port, int connectTimeout, int readTimeout) { 26 | super(host, port, connectTimeout, readTimeout); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /deviceconnector/src/main/java/com/sjl/deviceconnector/util/BluetoothUtils.java: -------------------------------------------------------------------------------- 1 | package com.sjl.deviceconnector.util; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.bluetooth.le.BluetoothLeScanner; 6 | import android.os.Build; 7 | 8 | import com.sjl.deviceconnector.entity.BluetoothScanResult; 9 | import com.sjl.deviceconnector.listener.BluetoothScanListener; 10 | 11 | import java.lang.reflect.Method; 12 | import java.util.ArrayList; 13 | import java.util.Collections; 14 | import java.util.List; 15 | import java.util.Set; 16 | 17 | import androidx.annotation.RequiresApi; 18 | 19 | /** 20 | * TODO 21 | * 22 | * @author Kelly 23 | * @version 1.0.0 24 | * @filename BluetoothUtils 25 | * @time 2023/3/3 15:04 26 | * @copyright(C) 2023 song 27 | */ 28 | public class BluetoothUtils { 29 | 30 | 31 | /** 32 | * 蓝牙适配器 33 | * 34 | * @return 35 | */ 36 | public static BluetoothAdapter getBluetoothAdapter() { 37 | return BluetoothAdapter.getDefaultAdapter(); 38 | } 39 | 40 | /** 41 | * 蓝牙适配器 42 | * 43 | * @return 44 | */ 45 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 46 | public static BluetoothLeScanner getBluetoothLeScanner() { 47 | BluetoothLeScanner bluetoothLeScanner = getBluetoothAdapter().getBluetoothLeScanner(); 48 | return bluetoothLeScanner; 49 | } 50 | 51 | /** 52 | * 判断蓝牙开关是否启用 53 | * 54 | * @return 55 | */ 56 | public static boolean isEnabled() { 57 | return getBluetoothAdapter().isEnabled(); 58 | } 59 | 60 | /** 61 | * 打开蓝牙 62 | */ 63 | public static void enable() { 64 | getBluetoothAdapter().enable(); 65 | } 66 | 67 | /** 68 | * 关闭蓝牙 69 | */ 70 | public static void disable() { 71 | getBluetoothAdapter().disable(); 72 | } 73 | 74 | /** 75 | * 获取已配对的蓝牙设备 76 | * 77 | * @return 78 | */ 79 | public static List