android started_Android 蓝牙的一些使用心得

最近在搞蓝牙串口开发,由于此前对蓝牙这块接触较少,所以在写项目时碰壁不少。在查阅了不少大神写的Android蓝牙项目技术文章后,于是写下本篇文章,算是自己这段时间对蓝牙些许了解的一个总结吧。

注:本篇章只介绍经典蓝牙,想了解低功耗蓝牙的请出门左转,哈哈~~~

蓝牙开发的基本流程为:开启-》搜索-》配对-》连接-》数据交换。其中配对成功之后系统会自动执行连接操作。

3778bf18aeb5bc15562e692ade3b784e.gif

权限

为了后面工作的不必要麻烦,首先在AndroidManifest.xml配置蓝牙权限

如果是6.0以上的系统,还需要增加定位相关的权限,还会延申到动态申请权限和GPS的检测

开启蓝牙

首先获取本地蓝牙适配器BluetoothAdapter 如果为空,说明设备不支持蓝牙。

public BluetoothAdapter getBtAdapter() {    BluetoothAdapter bluetoothAdapter =  BluetoothAdapter.getDefaultAdapter();    if (bluetoothAdapter == null){        Log.i(TAG, "getBtAdapter: 该设备不支持蓝牙");    }    return bluetoothAdapter;}

开启蓝牙的方式有两种,第一种简单粗暴,直接调用enable()方法即可,没有如何提示

bluetoothAdapter.enable();

第二种方式:Intent隐式打开,然后在onActivityResult方法中接收结果

 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BLUETOOTH); ... ...@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {    super.onActivityResult(requestCode, resultCode, data);    if (requestCode == REQUEST_ENABLE_BLUETOOTH) {        if (resultCode == Activity.RESULT_OK) {            Toast.makeText(BluetoothActivity.this, "打开蓝牙成功", Toast.LENGTH_SHORT).show();            tvStatu.setText("蓝牙已开启");            searchBtDevice();        } else {            Toast.makeText(BluetoothActivity.this, "打开蓝牙失败", Toast.LENGTH_SHORT).show();        }    }}
e0bdd0bc40d95b1670fd5f17a120f9e9.png

搜索蓝牙

扫描附近的蓝牙,开始扫描前前取消正在执行的搜索

    //取消当前正在搜索设备...    if (getBtAdapter().isDiscovering()) {        getBtAdapter().cancelDiscovery();    }    getBtAdapter().startDiscovery();

startDiscovery()是一个一步操作,一般会搜索12秒左右

广播接收扫描结果

注册广播

IntentFilter filter = new IntentFilter();filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //开始扫描filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描结束filter.addAction(BluetoothDevice.ACTION_FOUND);//搜索到设备filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //配对状态监听

接收广播

自定义一个广播接收类,监听设备扫描状态

/** * 搜索蓝牙广播接收器 */private class SearchReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        String action = intent.getAction();        if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {            mOnSearchDeviceListener.onStartDiscovery();            //开始搜索        } else if (BluetoothDevice.ACTION_FOUND.equals(action)) {            //发现设备            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);            if (mOnSearchDeviceListener != null) {                mOnSearchDeviceListener.onNewDeviceFound(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)) {            if (getBtAdapter() != null) {                getBtAdapter().cancelDiscovery();            }            //停止搜索            if (mOnSearchDeviceListener != null) {                mOnSearchDeviceListener.onSearchCompleted(mBondedList, mNewList);            }        }    }}

连接设备

我手上有个蓝牙转串口USB工具,所以就不写蓝牙服务端了。同时使用了SSCOM软件模拟数据接收和发送

51000a4c52163c319540566ed4ebbf77.png

建立连接

因为设备的连接时耗时的,所以我们需要另起一个线程ConnectThread,通过bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(BLUETOOTH_UUID));拿到BluetoothSocket,然后就可以connect了

需要注意的是 调用connect(),最好先释放BluetoothSocket

如果没配对过,会弹出配对请求,配对成功后系统会自动连接,设备连接成功后我们就可以处理数据的收发了

64ad90af0c7d373db61bb18930820995.png
   public class ConnectThread extends Thread {    private static final String TAG = "ConnectThread";    private final BluetoothAdapter mBluetoothAdapter;    private BluetoothSocket mmSocket;    private final String BLUETOOTH_UUID = "00001101-0000-1000-8000-00805F9B34FB";    public ConnectThread(BluetoothAdapter bluetoothAdapter, BluetoothDevice bluetoothDevice) {        this.mBluetoothAdapter = bluetoothAdapter;        BluetoothSocket tmpBluetoothSocket = null;        if (mmSocket != null) {            Log.i(TAG, "ConnectThread-->mmSocket != null先去释放");            try {                mmSocket.close();            } catch (IOException e) {                e.printStackTrace();            }        }        Log.i(TAG, "ConnectThread-->mmSocket != null已释放");        //1、获取BluetoothSocket        try {            //建立安全的蓝牙连接,会弹出配对框            tmpBluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(BLUETOOTH_UUID));        } catch (IOException e) {            Log.i(TAG, "ConnectThread-->获取BluetoothSocket异常!" + e.getMessage());        }        mmSocket = tmpBluetoothSocket;        if (mmSocket != null) {            Log.i(TAG, "ConnectThread-->已获取BluetoothSocket");        }    }    @Override    public void run() {        //连接之前先取消发现设备,否则会大幅降低连接尝试的速度,并增加连接失败的可能性        if (mBluetoothAdapter == null) {            Log.i(TAG, "ConnectThread:run-->mBluetoothAdapter == null");            return;        }        //取消发现设备        if (mBluetoothAdapter.isDiscovering()) {            mBluetoothAdapter.cancelDiscovery();        }        if (mmSocket == null) {            Log.i(TAG, "ConnectThread:run-->mmSocket == null");            return;        }        //2、通过socket去连接设备        try {            Log.i(TAG, "ConnectThread:run-->去连接...");            if (onBluetoothConnectListener != null) {                onBluetoothConnectListener.onStartConn();  //开始去连接回调            }            mmSocket.connect();  //connect()为阻塞调用,连接失败或 connect() 方法超时(大约 12 秒之后),它将会引发异常            if (onBluetoothConnectListener != null) {                onBluetoothConnectListener.onConnSuccess(mmSocket);  //连接成功回调                Log.i(TAG, "ConnectThread:run-->连接成功");            }        } catch (IOException e) {            Log.i(TAG, "ConnectThread:run-->连接异常!" + e.getMessage());            if (onBluetoothConnectListener != null) {                onBluetoothConnectListener.onConnFailure("连接异常:" + e.getMessage());            }            //释放            cancel();        }    }    /**     * 释放     */    public void cancel() {        try {            if (mmSocket != null && mmSocket.isConnected()) {                Log.i(TAG, "ConnectThread:cancel-->mmSocket.isConnected() = " + mmSocket.isConnected());                mmSocket.close();                mmSocket = null;                return;            }            if (mmSocket != null) {                mmSocket.close();                mmSocket = null;            }            Log.i(TAG, "ConnectThread:cancel-->关闭已连接的套接字释放资源");        } catch (IOException e) {            Log.i(TAG, "ConnectThread:cancel-->关闭已连接的套接字释放资源异常!" + e.getMessage());        }    }    private OnBluetoothConnectListener onBluetoothConnectListener;    public void setOnBluetoothConnectListener(OnBluetoothConnectListener onBluetoothConnectListener) {        this.onBluetoothConnectListener = onBluetoothConnectListener;    }    //连接状态监听者    public interface OnBluetoothConnectListener {        void onStartConn();  //开始连接        void onConnSuccess(BluetoothSocket bluetoothSocket);  //连接成功        void onConnFailure(String errorMsg);  //连接失败    }    } 

数据交换

设备连接成功后就可以进行数据交换了。我们可以写个ConnectedThread线程来处理数据的接收read()和发送write()。

public class ConnectedThread extends Thread {    private static final String TAG = "ConnectedThread";    private BluetoothSocket mmSocket;    private InputStream mmInStream;    private OutputStream mmOutStream;    //是否是主动断开    private boolean isStop = false;    //发起蓝牙连接的线程    private ConnectThread connectThread;    private OnReceiveListener onReceiveListener;    private Handler handler = new Handler(Looper.getMainLooper());    public void terminalClose(ConnectThread connectThread) {        isStop = true;        this.connectThread = connectThread;    }    public ConnectedThread(BluetoothSocket socket) {        mmSocket = socket;        InputStream tmpIn = null;        OutputStream tmpOut = null;        //使用临时对象获取输入和输出流,因为成员流是静态类型        try {            tmpIn = socket.getInputStream();            tmpOut = socket.getOutputStream();        } catch (IOException e) {            Log.i(TAG, "ConnectedThread-->获取InputStream 和 OutputStream异常!");        }        mmInStream = tmpIn;        mmOutStream = tmpOut;        if (mmInStream != null) {            Log.i(TAG, "ConnectedThread-->已获取InputStream");        }        if (mmOutStream != null) {            Log.i(TAG, "ConnectedThread-->已获取OutputStream");        }    }    @Override    public void run() {        //最大缓存区 存放流        byte[] buffer = new byte[1024 * 2];        //持续监听输入流直到发生异常        while (!isStop) {            try {                if (mmInStream == null) {                    Log.i(TAG, "ConnectedThread:run-->输入流mmInStream == null");                    break;                }                //先判断是否有数据,有数据再读取                if (mmInStream.available() != 0) {                    //2、接收数据                    /*                    为了demo正常运行,假设接收的数据长度为10;                    实际上我们可以自定义数据格式,如:STX + LENGTH(2hex bytes)+ CONTENT + ETX + LRC                     */                    int msgLen = 10;                    int readTotal = 0;                    int readCount;                    while (readTotal < msgLen) {                        readCount = mmInStream.read(buffer, readTotal, msgLen - readTotal);                        readTotal += readCount;                    }                    final byte[] respData = new byte[readTotal];                    System.arraycopy(buffer, 0, respData, 0, readTotal);                    if (onReceiveListener != null) {                        handler.post(new Runnable() {                            @Override                            public void run() {                                onReceiveListener.onReceiveDataSuccess(respData);  //成功收到消息                            }                        });                    }                }            } catch (final IOException e) {                Log.i(TAG, "ConnectedThread:run-->接收消息异常!" + e.getMessage());                if (onReceiveListener != null) {                    handler.post(new Runnable() {                        @Override                        public void run() {                            onReceiveListener.onReceiveDataError("接收消息异常:" + e.getMessage());  //接收消息异常                        }                    });                }                //关闭流和socket                boolean isClose = cancel();                if (isClose) {                    Log.i(TAG, "ConnectedThread:run-->接收消息异常,成功断开连接!");                }                break;            }        }        //关闭流和socket        boolean isClose = cancel();        if (isClose) {            Log.i(TAG, "ConnectedThread:run-->接收消息结束,断开连接!");        }    }    public void receive(OnReceiveListener onReceiveListener) {        this.onReceiveListener = onReceiveListener;    }    //发送数据    public void write(final byte[] bytes, final OnSendListener onSendListener) {        try {            if (mmOutStream == null) {                Log.i(TAG, "mmOutStream == null");                return;            }            mmOutStream.write(bytes);            Log.i(TAG, "写入成功:" + new String(bytes));            if (onSendListener != null) {                handler.post(new Runnable() {                    @Override                    public void run() {                        onSendListener.onSendSuccess(bytes);  //发送数据成功回调                    }                });            }        } catch (IOException e) {            Log.i(TAG, "写入失败:" + new String(bytes));            if (onSendListener != null) {                handler.post(new Runnable() {                    @Override                    public void run() {                        onSendListener.onSendError(bytes, "写入失败");  //发送数据失败回调                    }                });            }        }    }    /**     * 释放     *     * @return true 断开成功  false 断开失败     */    public boolean cancel() {        try {            if (mmInStream != null) {                mmInStream.close();  //关闭输入流            }            if (mmOutStream != null) {                mmOutStream.close();  //关闭输出流            }            if (mmSocket != null) {                mmSocket.close();   //关闭socket            }            if (connectThread != null) {                connectThread.cancel();            }            connectThread = null;            mmInStream = null;            mmOutStream = null;            mmSocket = null;            Log.i(TAG, "ConnectedThread:cancel-->成功断开连接");            return true;        } catch (IOException e) {            // 任何一部分报错,都将强制关闭socket连接            mmInStream = null;            mmOutStream = null;            mmSocket = null;            Log.i(TAG, "ConnectedThread:cancel-->断开连接异常!" + e.getMessage());            return false;        }    }}

以上就是经典蓝牙的基本操作流程了,剩下的就是需要注意一些细节了,比如设备搜索时长的控制、连接设备超时的处理等等。源码传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值