接着上篇蓝牙通信往下写,若有不对还请指出,大家共同进步。
Android开发之蓝牙通信(一)
Android开发之蓝牙通信(二)
Android开发之蓝牙通信(三)
前两篇提到了蓝牙开发的基本使用流程以及通信框架BluetoothKit,博主的蓝牙通信项目已经迭代了几个版本了,决定最后来做一个总结,主要是蓝牙通信协议相关,蓝牙通信一次限制传输字节大小,虽然蓝牙支持增量,但并不能保证成功率(可能硬件并没有增量),最好的解决办法就是切片,这里博主以下面协议为例:
type 请求类型:命令和一般请求
readHeadType 蓝牙读头类型
api 请求接口
pieceCount 当前分片是第几个
bodyLength 当前请求body长度
body 请求json
首先定义常量以及注解类型,这里以type为例,api 、readHeadType雷同(注解部分可以忽略)
/**
* Created by idea on 2017/11/13.
*/
public class Type {
public static final byte COMMAND = 0x00;
public static final byte REQUEST = 0x01;
}
/**
* Created by idea on 2017/11/13.
*/
@ByteDef({Type.COMMAND,Type.REQUEST})
@Retention(RetentionPolicy.SOURCE)
public @interface BluetoothType {
}
把协议文档编程代码后,开始编写传输的Message封装,为了使用方便,提供一个builder
/**
* Created by idea on 2017/11/13.
*/
public class Message {
private byte type;
private byte readHeadType;
private byte api;
private byte pieceCount;
private byte bodyLength;
private byte[] body = null;
private Message(){
}
public byte getType() {
return type;
}
public void setType(byte type) {
this.type = type;
}
public byte getReadHeadType() {
return readHeadType;
}
public void setReadHeadType(byte readHeadType) {
this.readHeadType = readHeadType;
}
public byte getApi() {
return api;
}
public void setApi(byte api) {
this.api = api;
}
public byte getPieceCount() {
return pieceCount;
}
public void setPieceCount(byte pieceCount) {
this.pieceCount = pieceCount;
}
public byte getBodyLength() {
return bodyLength;
}
public void setBodyLength(byte bodyLength) {
this.bodyLength = bodyLength;
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
public ArrayList<byte[]> getMessages(){
ArrayList<byte[]> result = new ArrayList<>();
if(body!=null&&body.length>15){
boolean flag = body.length % 15 == 0;
int count = body.length / 15 + (flag ? 0 : 1);
for (int i = 0; i < count; i++) {
byte[] message = null;
if (i == count - 1) {
message = new byte[body.length - 15 * i+5];
message[0] = type;
message[1] = readHeadType;
message[2] = api;
message[3] = (byte)i;
message[4] = (byte) (body.length-15*i);
System.arraycopy(body, 15 * i, message, 5, body.length - 15 * i);
result.add(message);
} else {
message = new byte[20];
message[0] = type;
message[1] = readHeadType;
message[2] = api;
message[3] = (byte) i;
message[4] = (byte)15;
System.arraycopy(body, 15 * i, message, 5, 15);
result.add(message);
}
}
}else{
int bodyLength = body==null?0:body.length;
byte[] message = new byte[5+bodyLength];
message[0] = type;
message[1] = readHeadType;
message[2] = api;
message[3] = 0x00;
message[4] = (byte) bodyLength;
if(bodyLength>0){
System.arraycopy(body,0,message,5,bodyLength);
}
result.add(message);
}
return result;
}
public interface IBuilder{
Message.IBuilder setType(@BluetoothType byte type);
Message.IBuilder setReadHeadType(@BluetoothReadHeadType byte type);
Message.IBuilder setApi(@BluetoothApi byte type);
Message.IBuilder setBody(byte[] bytes);
ArrayList<byte[]> creat();
}
public static class Builder implements Message.IBuilder {
private Message requstMessage;
private Builder(){
requstMessage = new Message();
}
public static Message.Builder newInstance() {
return new Message.Builder();
}
@Override
public Message.IBuilder setType(@BluetoothType byte type) {
requstMessage.setType(type);
return this;
}
@Override
public Message.IBuilder setReadHeadType(@BluetoothReadHeadType byte readHeadType) {
requstMessage.setReadHeadType(readHeadType);
return this;
}
@Override
public Message.IBuilder setApi(@BluetoothApi byte api) {
requstMessage.setApi(api);
return this;
}
@Override
public Message.IBuilder setBody(byte[] bytes) {
requstMessage.setBody(bytes);
return this;
}
@Override
public ArrayList<byte[]> creat() {
return requstMessage.getMessages();
}
}
}
代码调用发送消息示例如下
ArrayList<byte[]> messages = Message.Builder.newInstance()
.setType(Type.REQUEST)
.setApi(Api.USER_LOGIN)
.setReadHeadType(ReadHeadType.CHILD)
.setBody(body)
.creat();
public void write(ArrayList<byte[]> messages) {
for(int i=0;ithis.mClient.write(mac, Constant.SERVICE_UUID, Constant.WRITE_CHARA, messages.get(i), new BleWriteResponse() {
public void onResponse(int code) {
if (code == Code.REQUEST_SUCCESS) {
Logger.e("发送成功");
} else {
onFail("发送数据失败");
}
}
});
}
}
很多时候我们发送数据后都会需要响应,那么此时我们需要编写响应实体类Response
/**
* Created by idea on 2017/11/13.
*/
public class Response {
private boolean isResponseFinished;
private byte type;
private byte readHeadType;
private byte api;
private byte pieceCount ;
private byte bodyLength;
private byte[] body = null;
public byte getType() {
return type;
}
public void setType(byte type) {
this.type = type;
}
public byte getReadHeadType() {
return readHeadType;
}
public void setReadHeadType(byte readHeadType) {
this.readHeadType = readHeadType;
}
public byte getApi() {
return api;
}
public void setApi(byte api) {
this.api = api;
}
public byte getPieceCount() {
return pieceCount;
}
public void setPieceCount(byte pieceCount) {
this.pieceCount = pieceCount;
}
public byte getBodyLength() {
return bodyLength;
}
public void setBodyLength(byte bodyLength) {
this.bodyLength = bodyLength;
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
ArrayList<byte[]> responses = new ArrayList<>();
public boolean isResponseFinished() {
return isResponseFinished;
}
public void setIsResponseFinished(boolean isResponseFinished) {
this.isResponseFinished = isResponseFinished;
if(isResponseFinished){
formatBytes();
}
}
public Response(byte[] bytes) {
addResponse(bytes);
}
public void addResponse(byte[] bytes){
responses.add(bytes);
//TODO 这里根据服务器约定协议判定是否接收完成数据字段来设定
//setIsResponseFinished(boolean);
}
private Response formatBytes() {
type = responses.get(0)[0];
readHeadType = responses.get(0)[1];
api = responses.get(0)[2];
int length= 0;
for(int i=0;i4;
}
bodyLength = (byte) length;
body = new byte[bodyLength];
for(int i=0;i5,body,15*i,responses.get(i)[5]);
}
return this;
}
}
收到报文后解析如下
mClient.notify(mac, Constant.SERVICE_UUID, Constant.READ_CHARA, new BleNotifyResponse() {
public void onNotify(UUID service, UUID character, byte[] value) {
logE("接收到数据响应");
parseResponse(value);
}
public void onResponse(int code) {
//************略***************
}
});
private void parseResponse(byte[] value) {
if (responseMessage == null) {
responseMessage = new ResponseMessage(value);
} else if (!responseMessage.isResponseFinished()) {
responseMessage.addResponse(value);
}
if (responseMessage.isResponseFinished()) {
//TODO
logE("》》》》》》》》》》》》》》》》》》》》》》》》》 接受完成响应数据");
switch (responseMessage.getApi()) {
case Api.LOGIN:
if (responseMessage.getBody()[0] == 0x00) {
responseMessage = null;
//登陆成功
onSuccess();
} else {
onFail("上传数据失败");
}
break;
}
}
}
以上内容为蓝牙通信协议切片封装相关,蓝牙相关项目已经结束,接着继续搞音视频项目,待博主日后再来约几篇相关blog。