Android蓝牙开发

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

前言

这是我大二做的一个智能小车配套使用的APP用Android的蓝牙接口实现当时有些os相关的内容Thread之类还有一些Android接口、java语法我其实不是很理解。学了操作系统再来回顾一下并整理项目代码项目具有很高的复用性特别是蓝牙部分。

reference
项目参考了稚晖君的开源项目 https://github.com/peng-zhihui/BluetoothTouch
Android蓝牙开发官方文档 https://developer.android.google.cn/guide/topics/connectivity/bluetooth

设置蓝牙

这个在MainActivity中设置分为两步

  1. 获取BluetoothAdapter这是所有蓝牙活动的基础
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    // Get local Bluetooth adapter
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
        mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
    else
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    // If the adapter is null, then Bluetooth is not supported
    if (mBluetoothAdapter == null) {
        showToast("Bluetooth is not available", Toast.LENGTH_LONG);
        finish();
        return;
    }
}
  1. 使能蓝牙首先确定蓝牙是否启用若未启用调用startActivityForResult传递ACTION_REQUEST_ENABLE目标动作这些 大写变量 实际是Android定义好的字符串若以启用传入BluetoothHandlerBluetoothAdapter实例化我们自定义的蓝牙服务类BluetoothChatService后面慢慢介绍实际就是蓝牙接口封装起来的一个类
public void onStart()
{
	super.onStart();
    // If BT is not on, request that it be enabled.
    // setupChat() will then be called during onActivityResult
    if (!mBluetoothAdapter.isEnabled()) {
        Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
    } else
        setupBTService(); // Otherwise, setup the chat session
}

private void setupBTService()
{
    if (mChatService != null)
        return;
    if (mBluetoothHandler == null)
        mBluetoothHandler = new BluetoothHandler(this);
    mChatService = new BluetoothChatService(mBluetoothHandler, mBluetoothAdapter); // Initialize the BluetoothChatService to perform Bluetooth connections
}

连接设备

通过retry判断是否断线重连否则创建新连接像http协议有 ipv4ipv6 地址以太网协议有 MAC 地址蓝牙也有地址在蓝牙构建的网络中标识设备或者蓝牙对象。

private void connectDevice(Intent data, boolean retry)
{
    if (retry) {
        if (btDevice != null && !stopRetrying) {
            mChatService.start(); // This will stop all the running threads
            mChatService.connect(btDevice, btSecure); // Attempt to connect to the device
        }
    } else { // It's a new connection
        stopRetrying = false;
        mChatService.newConnection = true;
        mChatService.start(); // This will stop all the running threads
        if (data.getExtras() == null)
            return;

        // 获取设备蓝牙地址并连接设备
        String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); // Get the device Bluetooth address
        btSecure = data.getExtras().getBoolean(DeviceListActivity.EXTRA_NEW_DEVICE); // If it's a new device we will pair with the device
        btDevice = mBluetoothAdapter.getRemoteDevice(address); // Get the BluetoothDevice object

        mChatService.nRetries = 0; // Reset retry counter
        mChatService.connect(btDevice, btSecure); // Attempt to connect to the device
        showToast(getString(R.string.connecting), Toast.LENGTH_SHORT);
    }
}

BluetoothChatService.connect

connect方法的定义如下这里有mConnectThreadmConnectedThread两个用于连接和保持连接的线程先cancel之前的线程再重新连接start

public synchronized void connect(BluetoothDevice device, boolean secure)
{
    stopReading = true;
    
    // Cancel any thread attempting to make a connection
    if (mConnectThread != null) {
        mConnectThread.cancel();
        mConnectThread = null;
    }

    // Cancel any thread currently running a connection
    if (mConnectedThread != null) {
        mConnectedThread.cancel();
        mConnectedThread = null;
    }

    // Start the thread to connect with the given device
    mConnectThread = new ConnectThread(device, secure);
    mConnectThread.start();
    setState(STATE_CONNECTING);
}

BluetoothChatService.ConnectThread

这里final关键字修饰变量在初始化后不可更改。这里BluetoothSocket是用于收发数据的工具在网络通信的编程开发中很常见。这里仅实现了作为客户端进行连接坑补充作为服务端等待连接的线程实现。基本连接流程如下

  1. 使用BluetoothDevice调用createRfcommSocketToServiceRecord(UUID)获取蓝牙套接字BluetoothSocket 这个UUID是由服务端和客户端共同协商的RFCOMM就是蓝牙通信所使用的的信道
  2. 阻塞调用socketconnect()方法系统执行SDP查找具有指定UUID的设备若查找成功并且对方设备接受连接开始蓝牙通信共享RFCOMM信道若超时则抛出一个IOException
  3. 蓝牙连接完毕之后调用BluetoothChatService.connected启动保持连接线程或者说通信线程BluetoothChatService.ConnectedThread.
private class ConnectThread extends Thread
{
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;
    private String mSocketType;

    public ConnectThread(BluetoothDevice device, boolean secure)
    {
        mmDevice = device;
        BluetoothSocket tmp = null;
        mSocketType = secure ? "Secure" : "Insecure";

        // Get a BluetoothSocket for a connection with the
        // given BluetoothDevice
        try {
            if (secure)
                tmp = mmDevice.createRfcommSocketToServiceRecord(UUID_RFCOMM_GENERIC);
            else
                tmp = mmDevice.createInsecureRfcommSocketToServiceRecord(UUID_RFCOMM_GENERIC);
        } catch (IOException e) {
            if (D)
                Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
        }
        mmSocket = tmp;
    }

    public void run()
    {
        // Always cancel discovery because it will slow down a connection
        mAdapter.cancelDiscovery();
        newConnection = false;

        // Make a connection to the BluetoothSocket
        try {
            // This is a blocking call and will only return on a
            // successful connection or an exception
            mmSocket.connect();
        } catch (IOException e) {
            // Close the socket
            try {
                mmSocket.close();
            } catch (IOException e2) {
                if (D)
                    Log.e(TAG, "unable to close() " + mSocketType
                            + " socket during connection failure", e2);
            }
            if (!newConnection)
                connectionFailed();
            return;
        }

        // Reset the ConnectThread because we're done
        synchronized (BluetoothChatService.this) {
            mConnectThread = null;
        }

        // Start the connected thread
        connected(mmSocket, mmDevice, mSocketType);
    }

    public void cancel()
    {
        try {
            mmSocket.close();
        } catch (IOException e) {
            if (D)
                Log.e(TAG, "close() of connect " + mSocketType
                        + " socket failed", e);
        }
    }
}

传递数据

BluetoothChatService.ConnectedThread

成功连接蓝牙设备后双方均有一个BluetoothSocket这时可以分享信息数据传递流程如下

  1. 通过BluetoothSocket获取I/O对象InputStreamOutputStream;
  2. 通过read(byte[])write(byte[])进行以字节为单位数据读写实际上读要不断进行因此在线程的run()方法中实现这里读到后直接解析了更好地做法应该定义一个parser类负责数据的解析坑。
private class ConnectedThread extends Thread
{
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket, String socketType)
    {
        if (D)
            Log.d(TAG, "create ConnectedThread: " + socketType);
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the BluetoothSocket input and output streams
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            if (D)
                Log.e(TAG, "temp sockets not created", e);
        }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
        stopReading = false;
    }

    public void run()
    {
        if (D)
            Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;

        // Keep listening to the InputStream while connected
        while (!stopReading) {
            try {
                if (mmInStream.available() > 0) { // Check if new data is available
                    bytes = mmInStream.read(buffer); // Read from the InputStream

                    /***************  解释器  **************/

                    String readMessage = new String(buffer, 0, bytes);
                    String[] splitMessage = readMessage.split(",");

                    if (D) {
                        Log.i(TAG, "Received string: " + readMessage);
                        for (int i = 0; i < splitMessage.length; i++)
                            Log.i(TAG, "splitMessage[" + i + "]: " + splitMessage[i]);
                    }

                   // 命令解析...
                }
            } catch (IOException e) {
                if (D)
                    Log.e(TAG, "disconnected", e);
                if (!stopReading) {
                    cancel();
                    connectionLost();
                }
                return;
            }
        }
    }

    /**
     * Write to the connected OutStream.
     *
     * @param buffer The bytes to write
     */
    public void write(byte[] buffer)
    {
        try {
            mmOutStream.write(buffer);
        } catch (IOException e) {
            if (D)
                Log.e(TAG, "Exception during write", e);
        }
    }

    public void cancel()
    {
        stopReading = true;

        if (mmInStream != null) {
            try {
                mmInStream.close();
            } catch (Exception ignored) {
            }
        }
        if (mmOutStream != null) {
            try {
                mmOutStream.close();
            } catch (Exception ignored) {
            }
        }
        if (mmSocket != null) {
            try {
                mmSocket.close();
            } catch (Exception ignored) {
            }
        }
    }
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android