【161219丨Android学习笔记·蓝牙通讯

一、蓝牙概述

蓝牙协议分4层:核心协议层电缆替代协议层电话控制协议层采纳的其他协议层。最重要的是核心协议,包括基带,链路管理(LMP 负责蓝牙组件间连接的建立 设备的搜索配对连接),逻辑链路控制和适应协议(L2CAP 位于基带协议层上,属于数据链路层,是一个为高层传输和应用层协议屏蔽基带协议的适配协议

二、打开和关闭蓝牙设备

方式1:请求打开

Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, 1);

会出现蓝牙请求权限对话框

方式2:静默打开

要先在配置文件里添加权限



BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null) {
     // 设备不支持蓝牙
}
adapter.enable():  //打开
adapter.disable(); //关闭

三、搜索蓝牙设备

前提:必须先打开蓝牙模块
PS. 被动搜索:还要打开可见性

/* 使自己的蓝牙设备可见 */
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); 
// 可自定义可见时间(s),最大长度为3600。最小为0,表示始终可见。默认120,超过3600会被设为120

// 请求可见性的方法
startActivityForResult(discoverableIntent); 

3.1. 查询已配对设备

在搜索设备之前,要查询已配对的设备集,看看想要连接的设备是否已经配对。

// 获取已配对的设备集合
Set pairedDevices = mBluetoothAdapter.getBondedDevices();
// 如果有已配对的设备
if (pairedDevices.size() > 0) { 
    // 遍历已配对设备
    for (BluetoothDevice device : pairedDevices) { 
        // 把他们的名字和地址加入数组
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress()); 
    }
}

3.2. 发现设备

发现蓝牙设备,执行startDiscovery()方法。该过程是异步的,该方法将会立刻返回一个布尔值表明搜索是否已经开始。通常情况下,该搜索的过程调用12秒钟的查询,随后返回找到的设备。搜索到的设备通过广播回传

/* stap1. 定义和注册广播接收器 */
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 
// FOUND: 找到一个设备,会发送一个广播
this.registerReceiver(receiver, filter);

filter = new IntentFilter(BluetoothAdapter.ACTION_FINISHED); 
// FINISHED: 搜索结束,发送一个广播
this.registerReceiver(receiver, filter);

//记得在onDestroy()中反注销
/* step2. 开启搜索 */
public void onClick_Search(View view){
    if(myBluetoothAapter.isDiscovering()){
        //如果正在搜索,要停止。因为startDiscovery()不能重复调用
        myBluetoothAapter.cancelDiscovery();
    }
    myBluetoothAapter.startDiscovery();
}
/* step3. 重写接收器的onReceive方法 */
private final BroadcastReceiver receiver = new BroadcastReceiver() {
    @override
    public void onReceiver(Context context, Intent intent) {
        String action = intent.getAction();
        //当发现一个设备时
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
        // 判断设备是否已存在于已配对的设备集中
        if(BluetoothDevice.ACTION_FOUND.equals(action)){
            if(device.getBondState() != BluetoothDevice.BOND_BONDED){
                //将设备名称和地址添加到数组
                mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
            }
            else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
                //...搜索完成的 其他处理...
            }
        }
    }
}

四、连接蓝牙设备

使用BluetoothSocket和BluetoothServerSocket实现。

服务器端和客户端通过不同的方式获得BluetoothSocket。当一个连接接受(accept)的时候服务器端接收BluetoothSocket。而客户端则通过打开服务器端的RFCOMM通道得到BluetoothSocket。

一种实现技术是,应用程序同时实现客户端和服务器端。因此,每一个服务器端的程序拥有一个server socket并监听连接。
另一种是在一个应用中实现服务器端的功能,而另一个应用中实现客户端的部分。

PS. 如果两个设备之前并没有配对过,那么Android的框架将会自动进行配对的请求通知。因此当尝试进行连接时,你的应用并不需要关心两台设备是否已经配对。你的RFCOMM连接将会被阻塞,直到用户成功配对,或因为用户拒绝配对而取消,或者配对失败以及超时等。

UUID说明

UUID(全局唯一标识符 Universally Unique Identifier)实际上是一个格式8-4-4-4-12的字符串
UUID相当于Socket的端口,蓝牙地址相当于Socket的IP

/* 定义需要的组件 */
private BluetoothAdapter myBluetoothAapter;
private List bluetoothDevice = new ArrayList();

private final UUID MY_UUID = UUID.fromString("a8a5f732-5fc5-49f6-b2e7-bc472e8797db");
// 字符串符合UUID的格式即可
private final String NAME = "Bluetooth_Socket";

private BluetoothSocket clientSocket;
private BluetoothDevice device;

private OutputStream os; //客户端往服务端的输出流

4.1. 客户端的实现

/* 客户端 列表单击事件为例 */
public void onItemClick(AdapterView parent, View view, int position, long id){
    String s = mArrayAdapter.getItem(position);
    //解析蓝牙地址
    String address = s.substring(s.indexOf(":") + 1).trim();

    try{
        // 依旧 如果正在搜索则取消搜索
        if (myBluetoothAapter.isDiscovering()) {
            myBluetoothAapter.cancelDiscovery();
        }
        try{
            if(device == null){
                device = myBluetoothAapter.getRemoteDevice(address);
            }
            if(clientSocket == null){
                clientSocket = device.createRfcommSocketToServiceRecord(MY_UUID);

                // 开始连接
                clientSocket.connect();


                // 输出流
                os = clientSocket.getOutputStream();

            }
        }catch(Exception e){
            // 异常处理
        } 
        if(os != null){
            // 成功获取输出流
            os.write("发送信息".getBytes("utf-8"));
        }
    }
}

4.2. 服务端的实现

/* 服务端 监听客户端的线程类 */
private Handler handler = new Handler(){
    public void handleMessage(massage msg){
        Toast.makeText(Main.this, String.valueOf(msg.obj), Toast.LENGTH_LONG).show();
        super.handleMessage(msg);
    }
};
private class AcceptThread extends Thread{
    private BluetoothServerSocket serverSocket;
    private BluetoothSocket socket;
    private InputStream is;
    private OutputStream os;

    public AcceptThread(){
        try{
            serverSocket = myBluetoothAapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID)
        }catch(Exception e){
            // 异常处理
        }
    }

    //在run()方法中具体截获蓝牙消息
    public void run(){
        try{
            socket = serverSocket.accept();
            is = socket.getInputStream();
            os = socket.getOutputStream();

            // 无限从客户端接受
            while(true){
                byte[] buffer = new byte[128];
                int count = is.read(buffer);
                Message msg = new Message();
                msg.obj = new String(buffer, count, 0, "utf-8");
                handler.sendMessage(msg);
            }
        }catch(Exception e){
            // 异常处理
        }
    }
}
/* 在onCreat()里启动服务 */
acceptThread = new AcceptThread();
acceptThread.start();

四、问题总结

在实际项目里还是遇到了一些问题,记录如下:

4.1. 关于UUID

UUID的值必须是00001101-0000-1000-8000-00805F9B34FB。因为这个是android的API上面说明的,用于普通蓝牙适配器和android手机蓝牙模块连接。 一开始用了其他的,就连接不上了(┐「ε:)

你可能感兴趣的:(【161219丨Android学习笔记·蓝牙通讯)