之前公司做智能家居类型,其中做了一个智能衣柜项目,与衣柜通信就是用的蓝牙通信。一些操作一些简单的开关指令,蓝牙通信与socket是类似的。
步骤
- 清单文件注册权限
- 启动蓝牙服务(记得在清单文件中静态注册服务)
- 注册蓝牙广播(在蓝牙服务中动态注册蓝牙广播)
- 搜索,绑定,完成
- 退出app,停止服务,并在蓝牙服务的onDestory方法中取消注册蓝牙广播
不想写说明,只想贴代码
第一步:注册权限
<!--获取蓝牙信息状态权限-->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
第二步、第三步、第五步:启动蓝牙服务,注册蓝牙广播,停止服务
/**
* Author: 海晨忆.
* Date: 2018/1/4
* Desc:
*/
public class BluetoothService extends Service {
//得到蓝牙适配器
private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private BluetoothReceiver mReceiver;
@Override
public void onCreate() {
super.onCreate();
EventBus.getDefault().register(this);
if (mBluetoothAdapter != null) {
mReceiver = new BluetoothReceiver().setBluetoothAdapter(mBluetoothAdapter);
//注册设备被发现时的广播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
//注册一个搜索结束时的广播
IntentFilter filter2 = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter2);
startLinkBluetooth();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 开始连接蓝牙设备.
*/
private void startLinkBluetooth() {
if (null != mBluetoothAdapter) {
//判断蓝牙是否打开
if (!mBluetoothAdapter.isEnabled()) {
//若没打开则打开蓝牙
mBluetoothAdapter.enable();
}
mBluetoothAdapter.startDiscovery();
Log.v(Constants.HTTP_WZ, "正在扫描");
}
}
@Subscribe
@SuppressWarnings("unused")
public void handleMsg(BluetoothInfo bluetoothInfo) {
if (bluetoothInfo.isLink) {
startLinkBluetooth();
}
}
public static class BluetoothInfo {
private boolean isLink = false;
public BluetoothInfo setLink(boolean link) {
this.isLink = link;
return this;
}
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
if (mReceiver != null) {
mReceiver.unRegister();
unregisterReceiver(mReceiver);
}
}
}
这个就是我的蓝牙服务类,这个类的逻辑怎么走的呢?
- EventBus的东西我就不说了
- 首先获取蓝牙适配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- 初始化蓝牙广播,注册蓝牙广播
if (mBluetoothAdapter != null) {
mReceiver = new BluetoothReceiver().setBluetoothAdapter(mBluetoothAdapter);
//注册设备被发现时的广播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
//注册一个搜索结束时的广播
IntentFilter filter2 = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter2);
startLinkBluetooth();
}
- 开始准备连接蓝牙设备
/**
* 开始连接蓝牙设备.
*/
private void startLinkBluetooth() {
if (null != mBluetoothAdapter) {
//判断蓝牙是否打开
if (!mBluetoothAdapter.isEnabled()) {
//若没打开则打开蓝牙
mBluetoothAdapter.enable();
}
mBluetoothAdapter.startDiscovery();
Log.v(Constants.HTTP_WZ, "正在扫描");
}
}
- 上面4步蓝牙扫描就完成了,这里我还要说的是,在服务的onDestory方法里面,记得停止服务
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
if (mReceiver != null) {
mReceiver.unRegister();
unregisterReceiver(mReceiver);
}
}
- 再就是在清单文件里面静态注册蓝牙服务
<service android:name=".service.BluetoothService"/>
- 启动服务的方式,我用的是非绑定的方式,同样,记得停止服务。
Intent bluetoothService = new Intent(this, BluetoothService.class);
startService(bluetoothService);//启动蓝牙服务
stopService(bluetoothService);
@Nullable
@Override
public IBinder onBind(Intent intent) {
//非绑定方式,返回值为null
return null;
}
第四步:搜索,绑定,完成搜索
/**
* Author: 海晨忆.
* Date: 2018/1/4
* Desc: 蓝牙广播监听
*/
public class BluetoothReceiver extends BroadcastReceiver {
//衣柜的蓝牙名称
private static final String WARDROBE_NAME = "WARDROBE";
// 固定的UUID
private static final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";
private BluetoothSocket bluetoothSocket;
private BluetoothAdapter bluetoothAdapter;
private InputStream mInputStream;
private OutputStream outputStream;
private boolean isRunning = false;
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
if (action.equals(BluetoothDevice.ACTION_FOUND)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.v(Constants.HTTP_WZ, device.getName() + device.getAddress());
if (device.getBondState() == BluetoothDevice.BOND_BONDED
&& device.getName().equals(WARDROBE_NAME)) {
UUID uuid = UUID.fromString(SPP_UUID);
try {
bluetoothSocket = device.createRfcommSocketToServiceRecord(uuid);
Log.v(Constants.HTTP_WZ, "准备连接");
connect();
} catch (IOException e) {
e.printStackTrace();
}
}
} else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
if (!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
Observable.timer(2, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> {
if (null == bluetoothSocket || !bluetoothSocket.isConnected())
ToastUtils.showTipMsg(R.string.no_wardrobe);
});
}
}
}
private void connect() {
new Thread(() -> {
if (bluetoothSocket != null) {
bluetoothAdapter.cancelDiscovery();
try {
bluetoothSocket.connect();
Observable.just(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(integer -> ToastUtils.showTipMsg(R.string.link_wardrobe));
Log.v(Constants.HTTP_WZ, "连接成功");
mInputStream = bluetoothSocket.getInputStream();
Log.v(Constants.HTTP_WZ, "mInputSream:" + mInputStream.toString());
isRunning = true;
outputStream = bluetoothSocket.getOutputStream();
Log.v(Constants.HTTP_WZ, "outputStream:" + outputStream.toString());
BufferedReader br;
while (isRunning) {
br = new BufferedReader(new InputStreamReader(mInputStream, "utf-8"));
String s = br.readLine();
//acceptReply(s);
Log.v(Constants.HTTP_WZ, "收到的数据:" + s);
}
} catch (IOException e) {
e.printStackTrace();
try {
if (mInputStream != null) {
mInputStream.close();
Log.v(Constants.HTTP_WZ, "mInputSream.close()");
}
if (outputStream != null) {
outputStream.close();
Log.v(Constants.HTTP_WZ, "outputStream.close()");
}
if (bluetoothSocket != null) {
bluetoothSocket.close();
Log.v(Constants.HTTP_WZ, "socket.close()");
bluetoothSocket = null;
}
isRunning = false;
} catch (Exception e2) {
// TODO: handle exception
}
}
}
}).start();
}
public BluetoothReceiver setBluetoothAdapter(BluetoothAdapter adapter) {
this.bluetoothAdapter = adapter;
return this;
}
/**
* 反注册eventBus.
*/
public void unRegister() {
EventBus.getDefault().unregister(this);
}
}
这个就是我的蓝牙广播类,这个逻辑又是怎么走的呢?
- 前面服务里面注册的两个action,一个BluetoothDevice.ACTION_FOUND,还有一个BluetoothAdapter.ACTION_DISCOVERY_FINISHED,做了一个if判断,是发现了设备还是已经完成了扫描设备
- 发现设备之后,获取蓝牙信息,他这里是获取到一个蓝牙信息就会走一遍这个方法,并不是说一次获取一个列表。
- 找到了蓝牙设备之后就是连接了,伪代码讲解:
// 固定的UUID连接的时候需要uuid
private static final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";
//获取socket
BluetoothSocket bluetoothSocket =device.createRfcommSocketToServiceRecord(uuid);
//连接之前取消扫描,注意非空判断。adapter是在服务里面申明的,通过setBluetoothAdapter方法传过来的
bluetoothAdapter.cancelDiscovery();
//连接,这里是阻塞的方式,注意要新开线程连接
bluetoothSocket.connect();
//获取输入流对象和输出流对象
InputStream mInputStream = bluetoothSocket.getInputStream();
OutputStream outputStream = bluetoothSocket.getOutputStream();
//发送消息
private void sendInstruct(String msg) {
try {
if (null == bluetoothSocket || !bluetoothSocket.isConnected()) {
SocketUtils.reLinkBluetooth();
return;
}
Log.v(Constants.HTTP_WZ, "发送的数据-->" + msg + BluetoothInstruct.FINISH);
outputStream.write(msg.getBytes());
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
//接收消息,一次读一行,简单的蓝牙通信换行符作为结束标记
BufferedReader br;
while (isRunning) {
br = new BufferedReader(new InputStreamReader(mInputStream, "utf-8"));
String s = br.readLine();
acceptReply(s);
Log.v(Constants.HTTP_WZ, "收到的数据:" + s);
}
//异常的时候释放资源
try {
if (mInputStream != null) {
mInputStream.close();
Log.v(Constants.HTTP_WZ, "mInputSream.close()");
}
if (outputStream != null) {
outputStream.close();
Log.v(Constants.HTTP_WZ, "outputStream.close()");
}
if (bluetoothSocket != null) {
bluetoothSocket.close();
Log.v(Constants.HTTP_WZ, "socket.close()");
bluetoothSocket = null;
}
isRunning = false;
} catch (Exception e2) {
// TODO: handle exception
}