Android手机蓝牙开发及蓝牙通讯讲解
随着近两年可穿戴式产品逐渐进入人们的生活,蓝牙开发也成为了Android APP开发的一个重要模块,下面常州安卓APP开发公司紫竹云科技就来说一说蓝牙的这些API。
一、蓝牙介绍:
(一)、Bluetooth的由来及现状
蓝牙一词源于公元十世纪丹麦国王HaraldBlatand名字中的Blatand。Blatand的英文之意就是Blue tooth。这是因为这位让丹麦人引以为傲的国王酷爱吃蓝莓以至于牙龈都被染成蓝色。由于Blatand统一了丹麦和挪威,所以,作为无线通信技术的一种,蓝牙技术之所以取名Bluetooth可谓志向远大。
不过,在以Android为代表的智能机出现以前,蓝牙在早期智能机甚至功能机中一直扮演着“鸡肋”的角色。那么,随着无线通信技术的快速发展以及Android的普及,蓝牙技术在我们生活中的应用也越来越多,包括蓝牙耳机和鼠标,及蓝牙局域网应用(聊天、游戏等)。
(二)、蓝牙规范介绍
Core Specification(核心规范)
作用:用于规定蓝牙设备必须实现的通用功能和协议层次。它由软件和硬件模块组成,两个模块之间的信息和数据通过主机控制接口(HCI)的解释才能进行传递
核心规范是蓝牙协议家族的基础,自蓝牙技术联盟(Bluetooth SIG,Special Interest Group)在1999年颁布蓝牙核心规范1.0版本以来,到目前为止蓝牙SIG一共发布了七个重要版本。每一个版本都促使蓝牙技术朝着更快、更安全、更省电的方向发展。
二、蓝牙的用法
(一)、蓝牙API
Android APP所有关于蓝牙开发的类都在android.bluetooth包下,只有8个类:
BluetoothAdapter 本地蓝牙适配器
BluetoothClass 蓝牙类(主要包括服务和设备) BluetoothClass.Device 蓝牙设备类 BluetoothClass.Device.Major 蓝牙设备管理 BluetoothClass.Service 蓝牙服务类 BluetoothDevice 蓝牙设备(远程蓝牙设备) BluetoothServiceSocket 监听蓝牙连接的类 BluetoothSocket 蓝牙连接类
1、BluetoothAdapter :
表示本地的蓝牙适配器 (蓝牙射频)。BluetoothAdapter 是为所有蓝牙交互的入口点。它可以发现其他蓝牙设备、 查询绑定 (配对) 设备的列表、 实例化已知的 MAC 地址的BluetoothDevice(蓝牙设备) 和创建 BluetoothServerSocket 用于侦听来自其他设备的通信。直到我们建立bluetoothSocket连接之前,都要不断操作它 。BluetoothAdapter里的方法很多,常用的有以下几个:
2、BluetoothDevice
表示远程蓝牙设备。使用此类并通过BluetoothSocket类可以请求连接远程设备,或查询这台设备的信息如其名称、 地址、 类和绑定状态。createRfcommSocketToServiceRecord(UUIDuuid)
根据UUID创建并返回一个BluetoothSocket
,这个方法也是我们获取BluetoothDevice
的目的——创建BluetoothSocket
。这个类其他的方法,如getAddress()
、getName()
,同BluetoothAdapter
。
【备注:】蓝牙—RFCOMM协议
串口仿真协议(RFCOMM),RFCOMM是一个简单的协议,其中针对9针RS-232串口仿真附加了部分条款.可支持在两个蓝牙设备之间同时保持高达60路的通信连接.RFCOMM的目的是针对如何在两个不同设备上的应用之间保证一条完整的通信路径。
3、BluetoothServerSocket
表示打开服务器套接字侦听传入的请求 (类似于 TCP ServerSocket)。为了连接两台 Android 设备,一台设备必须用此类打开一个服务器套接字。当远程蓝牙设备向此设备发出连接请求时,而且当连接被接收时,BluetoothServerSocket 将返回连接的 BluetoothSocket。这个类有三个方法。
4、BluetoothSocket
跟BluetoothServerSocket相对,是客户端。表示一个蓝牙套接字 (类似于 TCP Socket) 的接口。这是一个允许应用程序与另一台蓝牙设备通过InputStream和OutputStream来交换数据的连接点。其一共5个方法,一般都会用到。
close():关闭 connect():连接 getInptuStream():获取输入流 getOutputStream():获取输出流 getRemoteDevice():获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备
5、BluetoothClass
描述的一般特征和蓝牙设备的功能。这是一整套只读的属性用于定义设备的主要和次要设备类和它的服务。然而,这并不是支持所有蓝牙配置文件和服务的设备,但很适用于获取设备类型
6、BluetoothProfile
表示一个蓝牙配置文件。蓝牙配置文件是基于蓝牙通信设备之间的无线接口规范。如免提规范(Hands-Free profile)
7、BluetoothHeadset
蓝牙耳机与手机一起使用配置文件 ,这包括蓝牙耳机和免提(v1.5) 的配置文件
8、BluetoothA2dp
定义了如何高质量的音频可以进行流式处理从一个设备到另一个通过蓝牙连接。”A2DP”代表先进音频分配协议
9、BluetoothHealth
表示控制蓝牙服务健康设备协议
10、BluetoothHealthCallback
BluetoothHealthCallback
一个抽象类,您使用来实现 BluetoothHealth
回调,你必须扩展此类并实现回调方法以接收有关更改的更新应用程序的注册和蓝牙通道状态。BluetoothHealthAppConfiguration
表示一个蓝牙健康第三方应用程序注册与远程蓝牙健康设备进行通信的应用程
11、BluetoothHealthAppConfiguration
表示一个蓝牙健康第三方应用程序注册与远程蓝牙健康设备进行通信的应用程序配置
12、BluetoothProfile.ServiceListener
通知 BluetoothProfile IPC 客户端界面时已被连接或断开服务 (即运行一个特定的配置文件内部服务)
(二)、使用蓝牙的权限
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
(三)、 UUID(universal unique identifier , 全局唯一标识符)
格式如下:UUID格式一般是”xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,可到http://www.uuidgenerator.com 申请。UUID分为5段,是一个8-4-4-4-12的字符串,这个字符串要求永不重复。String uuid = java.util.UUID.randomUUID().toString();
一般在创建Socket时需要UUID作为端口的唯一性,如果两台Android设备互联,则没有什么特殊的,如果让非Android的蓝牙设备连接Android蓝牙设备,则UUID必须使用某个固定保留的UUID
Android中创建UUID:UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
常用固定的UUID
蓝牙串口服务(SPP)
(四)、使用蓝牙的步骤:【五步曲】
1、获取本地蓝牙适配器
BluetoothAdapter mAdapter= BluetoothAdapter.getDefaultAdapter();
2、打开蓝牙
//弹出对话框提示用户是否打开 Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enabler, REQUEST_ENABLE); //不做提示,强行打开 // mAdapter.enable(); } 补充一下,使设备能够被搜索 Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); startActivityForResult(enabler,REQUEST_DISCOVERABLE);
3、搜索设备
1)mAdapter.startDiscovery()是第一步,可是你会发现没有返回的蓝牙设备,怎么知道查找到了呢? 2)定义BroadcastReceiver,代码如下 BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); //找到设备 if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (device.getBondState() != BluetoothDevice.BOND_BONDED) { Log.v(TAG, "find device:" + device.getName()+ device.getAddress()); } //搜索完成 }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setTitle("搜索完成"); if (mNewDevicesAdapter.getCount() == 0) { Log.v(TAG,"find over"); } }//执行更新列表的代码 } };
这样,没查找到新设备或是搜索完成,相应的操作都在上段代码的两个if里执行了,不过前提是你要先注册
BroadcastReceiver,具体代码如下,该段代码,一般写在onCreate()里.
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); registerReceiver(mReceiver, filter);
4、建立连接
首先Android sdk(2.0以上版本)支持的蓝牙连接是通过BluetoothSocket建立连接,服务器端(BluetoothServerSocket)和客户端(BluetoothSocket)需指定同样的UUID,才能建立连接,因为建立连接的方法会阻塞线程,所以服务器端和客户端都应启动新线程连接
1)服务器端: BluetoothServerSocket serverSocket = mAdapter. listenUsingRfcommWithServiceRecord(serverSocketName,UUID); serverSocket.accept();2)客户端: 还记得刚才在BroadcastReceiver获取了BLuetoothDevice么? BluetoothSocket clienSocket=dcvice. createRfcommSocketToServiceRecord(UUID); clienSocket.connect();
5、数据传递
通过以上操作,就已经建立的BluetoothSocket连接了,数据传递无非是通过流的形式
1)获取流 inputStream = socket.getInputStream(); outputStream = socket.getOutputStream();2)写出、读入 BluetoothServerSocket BluetoothServerSocket BluetoothAdapter.listenUsingRfcommWithServiceRecord(String name, UUID) 通过此方法监听BluetoothSocket的连接 BluetoothServerSocket.accept() 开始接收BluetoothSocket BluetoothServerSocket.close() 关闭服务 BluetoothSocket BluetoothSocket BluetoothDevice.createInsecureRfcommSocketToServiceRecord(UUID uuid) 通过此方法向指定的BluetoothDevice发送Socket连接 UUID:00001101-0000-1000-8000-00805F9B34FBconnect() 尝试连接boolean isConnected() 是否已连接,要求最低sdk 14+ BluetoothDevice getRemoteDevice() 获取当前正在或已连接的设备 InputStream getInputStream() 获取输入流 OutputStream getOutputStream() 获取输出流 在读取数据时用数据流 DataInputStream/DataOutputStream 基于Socket技术实现蓝牙聊天 蓝牙的配对 查找已配对的蓝牙设备 Set<BluetoothDevice> BluetoothAdapter.getBondedDevices()查找附件的蓝牙设备 BluetoothAdapter.startDiscovery()) BluetoothAdapter.isDiscovering() 是否正在查找 BluetoothAdapter.cancelDiscovery() 取消查找 注册广播接收器接收查到的设备信息 BluetoothAdapter.ACTION_DISCOVERY_STARTED 开始查找 BluetoothDevice.ACTION_FOUND 查找到蓝牙设备 BluetoothDevice.EXTRA_DEVICE 获取查找到的设备信息,此数据为ParcelableExtra,需要intent.getParcelableExtra()获取到BluetoothDevice对象 BluetoothAdapter.ACTION_DISCOVERY_FINISHED 查找结束 判断配对状态int BluetoothDevice.getBondState() 获取设备的配对状态 BluetoothDevice.BOND_BONDED 已配对 BluetoothDevice.BOND_BONDING 正在配对 BluetoothDevice.BOND_NONE 未配对 与指定未配对的设备配对 配对:通过反射获取BluetoothDevice的boolean createBond()方法,并执行 取消配对:通过反射获取BluetoothDevice的boolean removeBond()方法,并执行
三、蓝牙通信案例【实现服务器端与多个客户端通过蓝牙聊天】
(一)、服务器端程序制作步骤:
1、服务器线程
构造方法,创建BluetoothServerSocket对象;
通过bluetoothAdapter 的listenUsingRfcommWithServiceRecord()方法;
重写run()方法。死循环中,BluetoothServerSocket对象等待接收客户端的请求 ,如果建立连接,则将客户端线程放入客户端线程集合,并启动客户端线程。
BluetoothSocket clientSocket = serverSocket.accept(); ClientThread clientThread = new ClientThread(clientSocket); clientsList.add(clientThread); clientThread.start();
2、客户端线程
构造方法,获取客户端设备名称,并获取客户端套接字对象的输入、输出流对象;
clientName = clientSocket.getRemoteDevice().getName(); is = new DataInputStream(clientSocket.getInputStream()); os = new DataOutputStream(clientSocket.getOutputStream());
重写run()方法。死循环中,读取客户端发送过来的数据,追加到聊天记录中。启动发送线程,向所有客户端发送聊天数据。
String info = is.readUTF(); appendContent(info);new SendThread(info).start(); // 向所有客户端发送此数据 自定义sendInfo()消息发送方法。 os.writeUTF(info);
3、发送消息线程
构造方法,初始化要发送的信息数据;this.info = info;
重写run()方法。循环客户端线程集合,获取当前已连接的客户端,并逐一向其发送数据 。clientThread.sendInfo(info);
(二)、客户端程序制作步骤:
1、客户端线程
构造方法,初始化BluetoothDevice,连接选择的设备,建立连接,并获取客户端套接字对象的输入、输出流对象;
this.bluetoothDevice = device; clientSocket = bluetoothDevice .createRfcommSocketToServiceRecord(MainActivity.MY_UUID); clientSocket.connect(); is = new DataInputStream(clientSocket.getInputStream()); os = new DataOutputStream(clientSocket.getOutputStream());
重写run()方法。死循环中,读取客户端输入流数据,追加到聊天记录中。
String info = is.readUTF(); appendContent(info);
自定义sendInfo()消息发送方法。os.writeUTF(info);
2、发送消息线程
构造方法,初始化要发送的信息数据;this.info = info;
重写run()方法。调用连接线程的sendInfo()方法,向服务器端发送数据 。clientThread.sendInfo(info);