Android-蓝牙操作类库实现
Android对蓝牙的操作主要包含以下四个方面的内容:
- 打开手机的蓝牙
- 获取到已经绑定的蓝牙设备和当前范围内存在的蓝牙设备
- 蓝牙设备的配对
- 蓝牙设备之间数据的发送和接收
第一步,我们要打开蓝牙。
Android提供给我们了一个蓝牙的适配器类BluetoothAdapter,我们可以通过BluetoothAdapter.getDefaultAdapter()
来获取到它的一个实例。通过调用该类的公有方法enable(),我们可以实现打开蓝牙设备的操作,但需要注意的是,打开蓝牙这个操作是一个异步操作。如果想对蓝牙进行操作的话,请在蓝牙完全打开的时候进行。首先,我们判断当前设备是否有蓝牙设备,如果没有的话就抛出空指针异常,如果有就判断蓝牙设备是否打开,如果没有打开,就打开蓝牙设备。
private void requestEnableBt() {
if (mBluetoothAdapter == null) {
throw new NullPointerException(DEVICE_HAS_NOT_BLUETOOTH_MODULE);
}
if (!mBluetoothAdapter.isEnabled())
mBluetoothAdapter.enable();
}
第二步,获取到已经绑定的蓝牙设备和当前范围内存在的蓝牙设备
(1)获取到当前手机已经绑定的蓝牙设备
我们可以通过BluetoothAdapter的getBondedDevices()方法获取到BluetoothDevice
类的集合。我在这里把set集合转换成了ArrayList,方便我下一步的操作。
public ArrayList<BluetoothDevice> getBondedDevices(){
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
ArrayList<BluetoothDevice> bondedDevices = new ArrayList<>();
for(BluetoothDevice device:devices){
bondedDevices.add(device);
Log.d("TAG", "NAME :" + device.getAddress());
}
return bondedDevices;
}
(2)获取到当前范围内存在的蓝牙设备
扫描当前范围内的蓝牙设备,是通过动态注册一个BtReceiver广播来实现的。当注册了BtReceiver之后,每次Android发现一个蓝牙设备就会回调BtReceiver的onReceive()
方法,我们可以在onReceive()
方法里,处理我们自己的业务逻辑。如下是BtReceiver里的onReceive()
方法,在这里可以通过action
的类型来判断到当前收到的广播是发现了蓝牙
还是蓝牙已经搜索完毕
,并可在此回调不同的函数。注意:onReceive()
的形参intent
携带了搜寻到的蓝牙设备的信息,可通过intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
获取到蓝牙设备类的实例。
@Override
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_NONE) {
mNewList.add(device);
} else if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
mBondedList.add(device);
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
mOnSearchDeviceListener.onSearchCompleted(mBondedList, mNewList);
}
}
//动态注册发现蓝牙设备的广播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
mContext.registerReceiver(mReceiver, filter);
//动态注册搜索蓝牙设备结束的广播
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
mContext.registerReceiver(mReceiver, filter);
第三步,蓝牙设备的配对
蓝牙设备的配对时,一个做主机一个做从机,主机想要通信时,通过mac地址向想要连接的从机发出一个建立连接的请求。而从机呢则是开一个线程,一直监听是否有设备发出连接的请求,如果有,并且对方是我想要进行通信的对象,就进行配对。一旦配对成功,主机从机就可以在建立好的RFCOMM信道上进行通信了。
(1)主机发出连接请求
BluetoothDevice remoteDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(mac);
BluetoothSocket mSocket = remoteDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(Constants.STR_UUID));
mSocket.connect();
(2)从机接受连接请求
BluetoothServerSocket mServerSocket = null;
mServerSocket = BluetoothAdapter.getDefaultAdapter()
.listenUsingRfcommWithServiceRecord("BT", UUID.fromString(Constants.STR_UUID));
while (true) {
//死循环,监听是否有设备想要建立连接
socket = mServerSocket.accept();
}
第四步,蓝牙设备之间数据的发送和接收
(1)数据的发送
数据的发送,是建立连接之后获取到BluetoothSocket的实例的输出流,对其进行写操作实现的。
public void sendMessage(MessageItem item, OnSendMessageListener listener) {
WriteRunnable writeRunnable;
if( type == Constants.CONNECT_TYPE_CLIENT ){
writeRunnable = new WriteRunnable(item,listener,mConnectDeviceRunnable.getOutputStream());
mExecutorService.submit(writeRunnable);
}else if( type == Constants.CONNECT_TYPE_SERVER){
writeRunnable = new WriteRunnable(item,listener,mAcceptRunnable.getOutputStream());
mExecutorService.submit(writeRunnable);
}
}
因为在一个app里同时集成了Client端和Server端,故在发送数据前要先判断我当前做的是Client端还是Server端,如果是Client端就通过mConnectDeviceRunnable这个线程获取到输出流(因为在这个线程里实现的连接操作),如果是Server端就在mAcceptRunnable这个线程获取到输出流(因为在这个线程里完成的接受连接操作)。获取到输出流后,我们在WriteRunnable这个类里进行数据发送。
@Override
public void run() {
if (null != outputStream) {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
try {
writer.write(mMessageItem.getData());
writer.newLine();
writer.flush();
Message message = new Message();
message.what = HANDLER_WHAT_SEND_SUCCESS;
message.obj = mMessageItem.getData();
mHandler.sendMessage(message);
} catch (IOException e) {
mHandler.sendEmptyMessage(HANDLER_WHAT_SEND_FAILED);
}
}
}
mMessageItem是一个封装好的数据实体类,里面有我们想要发送出去的数据,我在这里将outputStream进行了
两层包装,然后通过writer的write方法将数据发送了出去,并通过mHandler告诉我的ui,数据发送成功了,
可以将此条数据显示出来。
(2)数据的接收
数据的接收,是建立连接之后获取到BluetoothSocket的实例的输入流,对其进行读操作实现的。
private void receiveMessage() {
OnReceiveMessageListener onReceiveMessageListener = new OnReceiveMessageListener() {
@Override
public void onNewLine(String s) {
Intent intent = new Intent(BroadcastType.BROADCAST_TYPE_RECEIVED_MESSAGE);
intent.putExtra("message", s);
mContext.sendBroadcast(intent);
}
@Override
public void onConnectionLost() {
Intent intent = new Intent(BroadcastType.BROADCAST_TYPE_CONNECTION_LOST);
mContext.sendBroadcast(intent);
}
@Override
public void onError(Exception e) {
}
};
ReadRunnable readRunnable;
if( type == Constants.CONNECT_TYPE_CLIENT){
readRunnable = new ReadRunnable(onReceiveMessageListener, mConnectDeviceRunnable.getInputStream());
mExecutorService.submit(readRunnable);
}else if( type == Constants.CONNECT_TYPE_SERVER){
readRunnable = new ReadRunnable(onReceiveMessageListener, mAcceptRunnable.getInputStream());
mExecutorService.submit(readRunnable);
}
}
同理,因为在一个app里同时集成了Client端和Server端,故在接收数据前要先判断我当前做的是Client端还是Server端,如果是Client端就通过mConnectDeviceRunnable这个线程获取到输入流(因为在这个线程里实现的连接操作),如果是Server端就在mAcceptRunnable这个线程获取到输入流(因为在这个线程里完成的接受连接操作)。获取到输入流后,我们在ReadRunnable这个类里进行数据接收。
@Override
public void run() {
if( null != mInputStream){
boolean runFlag = true;
int n;
char[] buffer = new char[1024];
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(mInputStream));
while (runFlag){
try {
if(mInputStream.available() <= 0){
continue;
}else {
try {
Thread.sleep(100);//这里等0.1秒是为了防止数据读取不完整
} catch (InterruptedException e) {
e.printStackTrace();
}
}
n = bufferedReader.read(buffer);
String s = new String(buffer, 0, n);
Log.d("TAG", "receive : "+ s);
mListener.onNewLine(s);
} catch (IOException e) {
e.printStackTrace();
runFlag = false;
mListener.onConnectionLost();
}
}
}
}
读取到了数据过后,我们通过函数mListener.onNewLine(s)将数据回调,然后在回调函数中,将数据通过广播发送出去,这样在注册了广播的地方,我们就可以处理收到的数据了。
Intent intent = new Intent(BroadcastType.BROADCAST_TYPE_RECEIVED_MESSAGE);
intent.putExtra("message", s);
mContext.sendBroadcast(intent);
这样一次蓝牙开发就结束了。
还有不明白的可以看我放在github上的源代码BluetoothHelper,里面有完整的demo和封装好的蓝牙类库。
如果对你有帮助的话,请star鼓励一下,哈哈哈!