之前公司做智能家居类型,其中做了一个智能衣柜项目,与衣柜通信就是用的蓝牙通信。一些操作一些简单的开关指令,蓝牙通信与socket是类似的。

步骤

  1. 清单文件注册权限
  2. 启动蓝牙服务(记得在清单文件中静态注册服务)
  3. 注册蓝牙广播(在蓝牙服务中动态注册蓝牙广播)
  4. 搜索,绑定,完成
  5. 退出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);
    }
  }
}

这个就是我的蓝牙服务类,这个类的逻辑怎么走的呢?

  1. EventBus的东西我就不说了
  2. 首先获取蓝牙适配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  1. 初始化蓝牙广播,注册蓝牙广播
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();
    }
  1. 开始准备连接蓝牙设备
/**
   * 开始连接蓝牙设备.
   */
  private void startLinkBluetooth() {
    if (null != mBluetoothAdapter) {
      //判断蓝牙是否打开
      if (!mBluetoothAdapter.isEnabled()) {
        //若没打开则打开蓝牙
        mBluetoothAdapter.enable();
      }
      mBluetoothAdapter.startDiscovery();
      Log.v(Constants.HTTP_WZ, "正在扫描");
    }
  }
  1. 上面4步蓝牙扫描就完成了,这里我还要说的是,在服务的onDestory方法里面,记得停止服务
@Override
  public void onDestroy() {
    super.onDestroy();
    EventBus.getDefault().unregister(this);
    if (mReceiver != null) {
      mReceiver.unRegister();
      unregisterReceiver(mReceiver);
    }
  }
  1. 再就是在清单文件里面静态注册蓝牙服务
<service android:name=".service.BluetoothService"/>
  1. 启动服务的方式,我用的是非绑定的方式,同样,记得停止服务。
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);
  }

}

这个就是我的蓝牙广播类,这个逻辑又是怎么走的呢?

  1. 前面服务里面注册的两个action,一个BluetoothDevice.ACTION_FOUND,还有一个BluetoothAdapter.ACTION_DISCOVERY_FINISHED,做了一个if判断,是发现了设备还是已经完成了扫描设备
  2. 发现设备之后,获取蓝牙信息,他这里是获取到一个蓝牙信息就会走一遍这个方法,并不是说一次获取一个列表
  3. 找到了蓝牙设备之后就是连接了,伪代码讲解:
// 固定的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
          }