欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android之蓝牙收发数据接口封装流程

程序员文章站 2024-03-25 08:00:57
...

本文的主要记录一下其中的蓝牙开发流程,留作以后整理备用;

文章主要讲述的是如何在第三方提供的蓝牙接口,根据自身定义的通讯协议(即收发数据封装)提供给用户进行二次开发的接口封装思路;

环境:第三方提供的蓝牙回调接口包括:蓝牙扫描、蓝牙连接等;

目标:根据自身定义的通讯协议,如获取版本信息、按键信息、控制亮灯等功能;封装成用户直接调用,而不需要了解自定义协议指定内容,从而获取到相应的数据即可;

现在就开始动手实现了,首先是第三方的蓝牙回调接口,

public interface IACSUtilityCallback{
   public void utilReadyForUse();
   public void didFoundPort(blePort newPort);
   public void didFinishedEnumPorts();
   public void didOpenPort(blePort port,Boolean bSuccess);
   public void didClosePort(blePort port);
   public void didPackageReceived(blePort port,byte[] packageToSend);
}

void sendData(byte[] data);

现在我们要实现的接口通过蓝牙向设备发送获取设备的版本信息指令,并同时把接收到的数据在处理完后返回给用户;

一、我们定义一个接口如下所示:

获取版本信息

String GetVer(){

//通过蓝牙发送指令
sendData();

//等待接收数据

//处理接收到的数据

//返回版本信息内容
return xxxx;
}

一开始这样的接口定义很符合我们所要实现的给用户使用的接口,但是在实际的使用中出现以下的问题;

1.等待接收数据:要等待多久,设置超时时间;通过重复发送,来提高接收数据的成功率?

2.第三方提供了蓝牙的接收数据的回调接口,所以我们就直接使用它的回调,把数据取出来就好了,但是什么时候才能收到数据呢?

3.(蓝牙每次返回的数据长度是20个字节,也就是每一包最长只能发20个字节,超过20个字节需要第二次发送)这样就面临了,如果数据超过20个字节,我们就没有接收到完整的数据,那我们要怎么样才能接收到完整的数据呢?(因为协议是自己定义的,所以不考虑怎么判断数据的完整性)

4.因为用户调用接口,相当于在主线程执行,从上述的3个问题,我们可能马上想到的是使用线程去处理,由此我们也就不能返回版本信息了;

回调接口看做一个接收缓存区,只要蓝牙有数据,就会通过回调方式,向外抛数据;

针对问题1、2、3:(设置一个全局变量,分别放在didPackageReceived里和while)

想法1:使用while来实现等待;(使用后发现,n--;都不能明确的保证能等待接收到的数据)

想法2:使用Thread.sleep来实现等待;(结果通过打印时间信息发现,你sleep了多久,后面整体也往后延时了多久)

想法3:使用 synchronized + wait + notify方式来实现等待以及处理(synchronized + wait + notify)的使用方式可以百度;

基于以上思考,我们针对1、2、3问题所采用的方式如下:(具体方式当然可以根据自身情况使用)

因为考虑到设备有按键操作,所以在设备连接上之后,我会直接开启一个数据解析线程,另外因为第三方提供蓝牙的回调,可以理解为一个接收线程,另外一个就是我们接口需要实现的发送等待接收线程;

接收线程如下:

byte[] buf = new byte[1024];
int bufCnt = 0;

private void didPackageReceive(ble port, byte[] data){

    StringBuilder sb = new StringBuilder();

    for (byte b : packageToSend) {
		sb.append("0x");
		if ((b & 0xff) <= 0x0f) {
			sb.append("0");
		}
		sb.append(Integer.toHexString(b & 0xff) + " ");

        if(Cnt > 1024){
            Cnt = 0;
        }
        buf[bufCnt++] = b; //此处可以使用OutputStream\InputStream流方式实现
	}  

}

解析线程如下:

class DoWithThread extends Thread{

    @Override
    public void run(){
        while(true){
            //根据自定义的协议格式进行解析;

            //还需要唤醒线程处理;
        }
    }
}

接口修改如下:

public interface GetVerCallback{        //通过回调函数的方式回传给主线程
    public void OnResult(String Info);
}


获取版本信息

void GetVer(GetVerCallback callback){

//通过蓝牙发送指令
sendData();

//等待接收数据(因为根据synchronized + wait + notify方式,wait会是当前正在运行的线程进入等待唤醒的状态,也就是主线程进入此状态)因此,需要独立开启一个线程

}

//等待接收数据线程
class WaitThread extends Thread{
    @Override
    public void run(){
        dowith = new DoWith();
        
        synchronized(dowith){
            try{
                dowith.wait(3000);    //如果未被notify唤醒,可以通过超时唤醒
            }catch(*){
                *******               //惯用格式不在写
            }
        }

        //处理接收到的数据

        //返回版本信息内容
        通过handler方式把结果通过回调接口返回给主线程
    }
}

这样就可以完整的实现接口的功能,用户只需要通过回调接口去获取信息即可;

 

注:其中代码部分比较简陋,如有疏漏欢迎指正,交流可在评论留言以及联系方式;

版权声明:https://blog.csdn.net/KingOTG/article/details/89390437