[置顶] Android开发之蓝牙通信(三)

移动开发 waitig 555℃ 百度已收录 0评论

接着上篇蓝牙通信往下写,若有不对还请指出,大家共同进步。

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;i<messages.size();i++){
            this.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;i<responses.size();i++){
            length+=responses.get(i).length-4;
        }

        bodyLength = (byte) length;

        body = new byte[bodyLength];
        for(int i=0;i<responses.size();i++){
            System.arraycopy(responses.get(i),5,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。


本文由【waitig】发表在等英博客
本文固定链接:[置顶] Android开发之蓝牙通信(三)
欢迎关注本站官方公众号,每日都有干货分享!
等英博客官方公众号
点赞 (0)分享 (0)