Android 经典蓝牙简单使用

之前在项目中碰到一个需求:通过android手机蓝牙连接红外线测温枪,并且测温枪的温度需要显示在手机上;
红外线测温枪照片
通过在网上查找各种方法以及查看Android官网中蓝牙介绍,终于实现了这个功能,android的蓝牙操作主要分为两种,一种是BLE蓝牙(低功耗蓝牙),另外一种是经典蓝牙,在这里是通过经典蓝牙方式实现

1.开发步骤

经典蓝牙开发步骤可以分为以下四步:

  • 设置蓝牙
  • 搜索附近蓝牙设备
  • 配对连接
  • 通信
2.添加权限

首先必须在清单文件中添加以下权限

    <!--蓝牙权限-->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

蓝牙搜索附近设备需要位置权限,如果是在android 6.0以上还需要对位置权限进行动态申请

3. 设置蓝牙
  • 打开蓝牙
        //蓝牙初始化,获取BluetoothAdapter
        BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
            //若没打开则打开蓝牙
            boolean isEnable = mBluetoothAdapter.enable();
            if (!isEnable) {
                //蓝牙权限被禁止,请在权限管理中打开
            }
        }
  • 注册用来接收蓝牙搜索的广播以及蓝牙搜索完毕的广播
        // 注册用以接收到已搜索到的蓝牙设备的receiver
        IntentFilter mFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        getActivity().registerReceiver(mBluetoothReceiver, mFilter);
        // 注册搜索完时的receiver
        mFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        getActivity().registerReceiver(mBluetoothReceiver, mFilter);
    // 广播接收发现蓝牙设备
    private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {

        @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);
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                //蓝牙搜索完成
                // 蓝牙搜索是非常消耗系统资源开销的过程,搜索完毕后应该及时取消搜索
                mBluetoothAdapter.cancelDiscovery();
            }
        }
    };
4. 搜索附近蓝牙设备

在确保蓝牙已经开启的情况下可以调用以下开始蓝牙扫描附近设备,开始扫描之后,每搜索到一个蓝牙设备都会发送一次BluetoothDevice.ACTION_FOUND广播,并且在搜索完毕之后会发送一次BluetoothAdapter.ACTION_DISCOVERY_FINISHED广播,我们可以在上面注册的广播接受者上面对搜索到的蓝牙进行记录并且在搜索完毕之后关闭蓝牙搜索。

if (mBluetoothAdapter != null && mBluetoothAdapter.startDiscovery()) {
       //启动蓝牙扫描附近设备
} else {
       //蓝牙扫描失败
}
5. 配对连接

选择其中一个搜索到的蓝牙设备进行连接,假设当前选择的蓝牙设备对象是mSelectBluetooth,开启一个子线程对该设备进行连接

    private BluetoothDevice mSelectBluetooth;
    private BluetoothSocket socket = null;
    private final String BLUETOOTH_UUID = "00001101-0000-1000-8000-00805F9B34FB";   //蓝牙通信的UUID,必须为这个,如果换成其他的UUID会无法通信

    /**
     * 蓝牙连接的线程
     */
    private class ClientThread extends Thread {

        private BluetoothDevice device;

        public ClientThread(BluetoothDevice device) {
            this.device = device;
        }

        @Override
        public void run() {

            try {
                socket = device.createRfcommSocketToServiceRecord(UUID.fromString(BLUETOOTH_UUID));

                Log.d(TAG, "连接服务端...");
                socket.connect();

                Log.d(TAG, "连接建立.");

                ReadThread readThread = new ReadThread();  //连接成功后开启读取该蓝牙设备数据的线程
                readThread.start();
            } catch (Exception e) {
                //连接已断开      
            }
        }
    }

    //连接设备和读取数据都需要新启线程在子线程中进行,因为过程比较耗时,如果不在子线程影响效率和阻塞UI。代码我就不做解释了,上面注释写的很清楚了。下面贴读取数据的操作:
    //读写线程
    private class ReadThread extends Thread {
        @Override
        public void run() {
            byte[] buffer = new byte[1024];
            int bytes;
            InputStream mmInStream = null;   //建立输入流读取数据
            try {
                mmInStream = socket.getInputStream();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            while (true) {  //无限循环读取数据
                try {
                    // Read from the InputStream
                    if ((bytes = mmInStream.read(buffer)) > 0) {
                        byte[] buf_data = new byte[bytes];
                        for (int i = 0; i < bytes; i++) {
                            buf_data[i] = buffer[i];
                        }

                        //String s = bytesToHexString(buf_data);
                        System.arraycopy(buffer, 0, buf_data, 0, bytes);
                        final int finalBytes = bytes;

                        final String text = TemperatureUtil.getHexString(buf_data, 0, finalBytes);

                        Log.d(TAG, "读取到的温度为: " + TemperatureUtil.getResultTemperature(text));

                    }
                } catch (Exception e) {
                    try {
                        mmInStream.close();
                    } catch (IOException e1) {

                    }

                    break;  //异常的时候break跳出无限循环
                }
            }
        }
    }

其中TemperatureUtil工具类代码如下:

public class TemperatureUtil {
    final static char[] HEX_CHAR_TABLE = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70};

    public static String getHexString(byte[] paramArrayOfByte, int paramInt1, int paramInt2)
            throws UnsupportedEncodingException {
        StringBuffer localStringBuffer = new StringBuffer();
        int j = paramInt1;
        for (; ; ) {
            if (j >= paramInt1 + paramInt2) {
                return localStringBuffer.toString();
            }
            int k = paramArrayOfByte[j] & 0xFF;
            localStringBuffer.append(HEX_CHAR_TABLE[(k >>> 4)]);
            localStringBuffer.append(HEX_CHAR_TABLE[(k & 0xF)]);
            localStringBuffer.append(" ");
            j += 1;
        }
    }

    /**
     * 获取温度值
     *
     * @param hexString
     * @return
     */
    public static String getResultTemperature(String hexString) {
        try {
            String[] split = hexString.split(" ");
            int tmpInt = Integer.parseInt(split[1] + split[2], 16);
            String result = new String(tmpInt * 0.1f + "").substring(0, 4);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

在需要连接的地方调用以下代码即可

//开启线程进行蓝牙连接
new Thread(new ClientThread(mSelectBluetooth)).start();
5. 通信

在手机通过蓝牙连接上测温枪之后,线程ReadThread会阻塞的等待测温枪数据传输,测温枪每测一次温度,都可以在线程ReadThread中获取,不过从测温枪获取到的体温数据是都是十六进制的,这里可以通过上面的根据类TemperatureUtil转化成摄氏温度。

6.注意事项
  • 测温枪如果长时间没有使用的情况下会进入休眠状态,此时蓝牙连接会断开,等到重新开启后需要重新连接,这里可以通过序列化将蓝牙对象保存下来,等到再次连接的时候就不需要重新开始蓝牙搜索,可以直接连接
  • 在页面退出时,记得将线程退出,并且将广播反注册

猜你喜欢

转载自blog.csdn.net/weixin_44247225/article/details/87253096
今日推荐