Android-蓝牙操作类库实现

作者: houlucky | 来源:发表于2016-11-22 09:13 被阅读551次

    Android对蓝牙的操作主要包含以下四个方面的内容:

    1. 打开手机的蓝牙
    2. 获取到已经绑定的蓝牙设备和当前范围内存在的蓝牙设备
    3. 蓝牙设备的配对
    4. 蓝牙设备之间数据的发送和接收

    第一步,我们要打开蓝牙。

    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鼓励一下,哈哈哈!

    相关文章

      网友评论

        本文标题:Android-蓝牙操作类库实现

        本文链接:https://www.haomeiwen.com/subject/wzgupttx.html