美文网首页BLE
BluetoothChat(蓝牙聊天室),对官方Demo的解读

BluetoothChat(蓝牙聊天室),对官方Demo的解读

作者: 仕明同学 | 来源:发表于2020-04-11 22:23 被阅读0次

    image.png

    这个应用程序允许两个Android设备进行双向文本聊天蓝牙。它演示了所有基本的蓝牙API功能,例如:

    (1) 正在扫描其他蓝牙设备
    (2) 为配对的蓝牙设备查询本地蓝牙适配器
    (3) 建立RFCOMM信道/套接字
    (4) 连接到远程设备
    (5) 通过蓝牙传输数据

    • 权限
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
        <uses-permission android:name="android.permission.BLUETOOTH" />
    
    扫描其他蓝牙设备
    • 通过系统的类获取蓝牙设备的列表
       mBtAdapter = BluetoothAdapter.getDefaultAdapter();
            Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
    
    • 如果正在扫描 就取消
     if (mBtAdapter.isDiscovering()) {
                mBtAdapter.cancelDiscovery();
            }
           mBtAdapter.startDiscovery();
    
    • 最主要的就是获取17为的MAC地址
       String info = ((TextView) v).getText().toString();
     String address = info.substring(info.length() - 17);
    
    • 同时注册广播,发现了设备
           IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
            this.registerReceiver(mReceiver, filter);
      filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
            this.registerReceiver(mReceiver, filter);
    
    • 获取到mac地址就可以获取蓝牙的设备信息
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    
    首先开启一个子线程监听Socket中的消息 AcceptThread 获取安全和安全的Socket ,我觉得可以理解一个应答的线程
     private final BluetoothServerSocket mmServerSocket;
            private String mSocketType;
    
            public AcceptThread(boolean secure) {
                BluetoothServerSocket tmp = null;
                mSocketType = secure ? "Secure" : "Insecure";
    
                // Create a new listening server socket
                try {
                    if (secure) {
                        tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
                                MY_UUID_SECURE);
                    } else {
                        tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
                                NAME_INSECURE, MY_UUID_INSECURE);
                    }
                } catch (IOException e) {
                    Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
                }
                mmServerSocket = tmp;
                mState = STATE_LISTEN;
            }
    
    • run的方法 关键是 connected(socket, socket.getRemoteDevice(),mSocketType);
      public void run() {
                Log.d(TAG, "Socket Type: " + mSocketType +
                        "BEGIN mAcceptThread" + this);
                setName("AcceptThread" + mSocketType);
    
                BluetoothSocket socket;
    
                // Listen to the server socket if we're not connected
                while (mState != STATE_CONNECTED) {
                    try {
                        // This is a blocking call and will only return on a
                        // successful connection or an exception
                        socket = mmServerSocket.accept();
                    } catch (IOException e) {
                        Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
                        break;
                    }
    
                    // If a connection was accepted
                    if (socket != null) {
                        synchronized (BluetoothChatService.this) {
                            switch (mState) {
                                case STATE_LISTEN:
                                case STATE_CONNECTING:
                                    // Situation normal. Start the connected thread.
                                    connected(socket, socket.getRemoteDevice(),
                                            mSocketType);
                                    break;
                                case STATE_NONE:
                                case STATE_CONNECTED:
                                    // Either not ready or already connected. Terminate new socket.
                                    try {
                                        socket.close();
                                    } catch (IOException e) {
                                        Log.e(TAG, "Could not close unwanted socket", e);
                                    }
                                    break;
                            }
                        }
                    }
                }
                Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
    
            }
    
    • connect的方法 ,直接和 ConnectedThread 产生关系,这样就可以发送消息了
      public synchronized void connected(BluetoothSocket socket, BluetoothDevice
                device, final String socketType) {
            Log.d(TAG, "connected, Socket Type:" + socketType);
    
            // Cancel the thread that completed the connection
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
    
            // Cancel any thread currently running a connection
            if (mConnectedThread != null) {
                mConnectedThread.cancel();
                mConnectedThread = null;
            }
    
            // Cancel the accept thread because we only want to connect to one device
            if (mSecureAcceptThread != null) {
                mSecureAcceptThread.cancel();
                mSecureAcceptThread = null;
            }
            if (mInsecureAcceptThread != null) {
                mInsecureAcceptThread.cancel();
                mInsecureAcceptThread = null;
            }
    
            // Start the thread to manage the connection and perform transmissions
            mConnectedThread = new ConnectedThread(socket, socketType);
            mConnectedThread.start();
    
            // Send the name of the connected device back to the UI Activity
            Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
            Bundle bundle = new Bundle();
            bundle.putString(Constants.DEVICE_NAME, device.getName());
            msg.setData(bundle);
            mHandler.sendMessage(msg);
            // Update UI title
            updateUserInterfaceTitle();
        }
    
    如何发送消息 ?

    1、获取字节数组 ,通过ChatService的方式

        byte[] send = message.getBytes();
                mChatService.write(send);
    

    2、通过在子线程中写入到 OutputStream 流中

       private final OutputStream mmOutStream;
    

    3、将发送的消息共享回UI活动 在子线程共享到主线程 mmOutStream

      public void write(byte[] buffer) {
                try {
                    mmOutStream.write(buffer);
    
                    // 将发送的消息共享回UI活动 在子线程共享到主线程
                    mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
                            .sendToTarget();
                } catch (IOException e) {
                    Log.e(TAG, "Exception during write", e);
                }
            }
    

    4、outputStream其实是通过BluetoothSocket 获取而来

        tmpIn = socket.getInputStream();
                    tmpOut = socket.getOutputStream();
    

    5、读取到的消息回调到主线程中, 这就完成信息的读取和输入

     public void run() {
                Log.i(TAG, "BEGIN mConnectedThread");
                byte[] buffer = new byte[1024];
                int bytes;
    
                // Keep listening to the InputStream while connected
                while (mState == STATE_CONNECTED) {
                    try {
                        // Read from the InputStream
                        bytes = mmInStream.read(buffer);
    
                        // 把读取的消息回调到主线程中
                        mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
                                .sendToTarget();
                    } catch (IOException e) {
                        Log.e(TAG, "disconnected", e);
                        connectionLost();
                        break;
                    }
                }
            }
    
    
    • 对官方Demo的解读
    • GitHub有些自己的注释
    • 底层就是一个Socket,如果用Java做过,就很好理解,关键是如何使用子线程去避免ANR?这个需要去思考
    • Socket是一个好性能的东西,所以需要思考如何关闭
    • Socket只有一个实例,所以当某一步发生了失败的话,都需要把它 close mmServerSocket.close();
    • 使用BluetoothAdapter类的listenUsingRfcommWithServiceRecord方法来新建一个ServerSocket。在listenUsingRfcommWithServiceRecord中有一个参数叫做UUID,UUID(Universally Unique Identifier)是一个128位的字符串ID,被用于唯一标识我们的蓝牙服务
    • 线程取消还要使用java上null回收标志 if (mInsecureAcceptThread != null) { mInsecureAcceptThread.cancel(); mInsecureAcceptThread = null; }
    • UUID可以在网上去申请
    • 如果我要发语音呢? 那是不是有点像对讲机了 但是作为安卓应用好像有点大材小用了 。。。是吧!自己感觉 哈哈

    相关文章

      网友评论

        本文标题:BluetoothChat(蓝牙聊天室),对官方Demo的解读

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