学习笔记之经典蓝牙开发

    xiaoxiao2021-03-25  28

    概述

    去年公司的项目需要用到蓝牙开发的相关内容,因此查阅了Google官方文档的内容并进行二次整理。本文只涉及经典蓝牙(Classic Bluetooth)的开发,并不涉及低功耗蓝牙(BLE)的开发,本文应用场景为蓝牙单聊。

    官方项目:GoogleSamples-android-BluetoothChat

    Bluetooth Low Energy:学习笔记之低功耗蓝牙开发

    效果图A-小米4手机

    效果图B-魅蓝2手机

    权限

    经典蓝牙需要用到2个权限

    允许程序连接到已配对的蓝牙设备<uses-permission android:name="android.permission.BLUETOOTH"/> 允许程序发现和配对蓝牙设备<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

    在AndroidManifest加上这个2个权限

    打开蓝牙

    (1)判断该设备是否支持蓝牙、蓝牙功能是否被打开

    public boolean openBluetooth() { if (adapter == null) { Toast.makeText(this, "该设备不支持蓝牙", Toast.LENGTH_LONG).show(); return false; } else { if (!adapter.isEnabled()) { // 打开蓝牙    // 设置蓝牙可见性,最多300秒    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivityForResult(intent, 0); return false; } } return true; }

    (2)在onActivityResult方法处理回调结果

    @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (resultCode) { case RESULT_OK: Toast.makeText(this, "开启蓝牙成功", Toast.LENGTH_SHORT).show(); break; case RESULT_CANCELED: Toast.makeText(this, "开启蓝牙失败", Toast.LENGTH_SHORT).show(); break; } }

    发现/搜索设备

    (1)通过getBondedDevice()方法的获得已经匹配的设备。然后调用startDiscovery()方法来开始设备的搜索

    public void addBluetooth(ArrayList<BluetoothDevice> mainList) { Set<BluetoothDevice> pairedDevices = adapter.getBondedDevices(); if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { if (!mainList.contains(device)) { mainList.add(device); } } } // 寻找蓝牙设备,android会将查找到的设备以广播形式发出去    adapter.startDiscovery(); }(2)通过广播的形式来添加搜索附近蓝牙设备 private BroadcastReceiver receiver = 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); /*if (device.getBondState() == BluetoothDevice.BOND_BONDED) {//已匹配的设备 if (device != null) { if (!mainList.contains(device)) { mainList.add(device); lvAdapter.notifyDataSetChanged(); } } }*/ if(device!=null){ if (!mainList.contains(device)) {//未包含则添加 mainList.add(device); lvAdapter.notifyDataSetChanged(); } } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//搜索结束 stopAnimation(fab); } Log.e("BLUE", "size = " + mainList.size()); lvAdapter.notifyDataSetChanged(); } };(3)动态注册并设置广播,记得销毁activity时销毁

    private void initReceiver() { IntentFilter intentFilter = new IntentFilter();// 设置广播信息过滤    intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//扫描发现设备广播 intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//设备连接状态改变的广播 intentFilter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//扫描模式变化广播 intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//开关模式变化广播 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描结束 // 注册广播接收器,接收并处理搜索结果    registerReceiver(receiver, intentFilter); }

    设备配对与解除

    注意事项

    ①蓝牙设备之间自动配对,需要两个设备都安装进行配对的apk

    ②由于BluetoothDevice配对的方法都是hide的,所以我们需要通过反射调用被隐藏的方法(现在基本都是通用的工具,网上模板基本一样,参考这里)

    (1)配对事件

    protected void connectDevice(BluetoothDevice mBluetoothDevice) { try { if (mBluetoothDevice.getBondState() == BluetoothDevice.BOND_NONE) {//未配对 ClsUtils.createBond(mBluetoothDevice.getClass(), mBluetoothDevice); }else if(mBluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDING){//配对中 ClsUtils.cancelBondProcess(BluetoothDevice.class, mBluetoothDevice); } else { ClsUtils.removeBond(BluetoothDevice.class, mBluetoothDevice); Toast.makeText(this, "蓝牙配对解除", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } }

    (2)蓝牙配对广播接收

    public class BuletoothPairingReceiver extends BroadcastReceiver { String strPsw = "0000"; final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ACTION_PAIRING_REQUEST)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.getBondState() != BluetoothDevice.BOND_BONDED) { try { abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。 //确认配对 ClsUtils.setPairingConfirmation(device.getClass(), device, true); //调用setPin方法进行配对... boolean ret = ClsUtils.setPin(device.getClass(), device, strPsw); // Toast.makeText(context, "配对信息" + device.getName()+"=="+ret, Toast.LENGTH_LONG).show(); } catch (Exception e) { Toast.makeText(context, "请求连接错误...", Toast.LENGTH_LONG).show(); } } } } }

    (3)AndroidManifest静态注册广播

    <receiver android:name=".receiver.PairingRequest"> <intent-filter android:priority="1000"> <action android:name="android.bluetooth.device.action.PAIRING_REQUEST" /> </intent-filter> </receiver>

    (4)设置蓝牙设备可见性

    static public void setDiscoverableTimeout(BluetoothAdapter adapter, int timeout) { try { Method setDiscoverableTimeout = BluetoothAdapter.class.getMethod("setDiscoverableTimeout", int.class); setDiscoverableTimeout.setAccessible(true); Method setScanMode = BluetoothAdapter.class.getMethod("setScanMode", int.class, int.class); setScanMode.setAccessible(true); setDiscoverableTimeout.invoke(adapter, timeout); setScanMode.invoke(adapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, timeout); } catch (Exception e) { e.printStackTrace(); } }

    (5)关闭蓝牙设备可见性

    static public void closeDiscoverableTimeout(BluetoothAdapter adapter) { try { Method setDiscoverableTimeout = BluetoothAdapter.class.getMethod("setDiscoverableTimeout", int.class); setDiscoverableTimeout.setAccessible(true); Method setScanMode = BluetoothAdapter.class.getMethod("setScanMode", int.class, int.class); setScanMode.setAccessible(true); setDiscoverableTimeout.invoke(adapter, 1); setScanMode.invoke(adapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE, 1); } catch (Exception e) { e.printStackTrace(); } }

    连接通信

    (1)基本流程

    ①主动连接:通过调用BluetoothDevice的createRfcommSocketToServiceRecord(UUID)方法,获得一个BluetoothSocket对象。通过调用connect()方法来初始化连接。在这个调用中,为了找到匹配的UUID,系统会在远程的设备上执行一个SDP查询。如果查询成功,并且远程设备接收了该连接请求,那么它会在连接期间共享使用RFCOMM通道,并且connect()方法会返回。这个方法是一个阻塞调用。如果因为某些原因,连接失败或连接超时(大约在12秒之后),就会抛出一个异常。 

    ②被动连接:通过调用BluetoothAdapter的listenUsingRfcommWithServiceRecord(String name, UUID uuid),获得BluetoothServerSocket对象。该方法中的String参数是一个可识别的你的服务端的名称,系统会自动的把它写入设备上的Service Discovery Protocol(SDP)数据库实体(该名称是任意的,并且可以简单的使用你的应用程序的名称)。UUID参数也会被包含在SDP实体中,并且是跟客户端设备连接的基本协议。也就是说,当客户端尝试跟服务端连接时,它会携带一个它想要连接的服务端能够唯一识别的UUID。只有在这些UUID完全匹配的情况下,连接才可能被接收。通过调用accept()方法,启动连接请求。这是一个阻塞调用。只有在连接被接收或发生异常的情况下,该方法才返回。只有在发送连接请求的远程设备所携带的UUID跟监听服务套接字所注册的一个UUID匹配的时候,该连接才被接收。连接成功,accept()方法会返回一个被连接的BluetoothSocket对象。 除非你想要接收其他连接,否则在接收到一个连接套接字之后,立即调用BluetoothServerSocket对象的close()方法。该方法会释放服务套接字以及它所占用的所有资源,但不会关闭被连接的已经有accept()方法所返回的BluetoothSocket对象。

    ③数据传输:当你成功的连接了两个(或更多)设备时,每一个设备都有一个被连接的BluetoothSocket对象。使用BluetoothSocket对象分别通过getInputStream()和getOutputStream()方法来获得通过套接字来处理传输任务的InputStream和OutputStream对象; 用read(byte[])和write(byte[])方法来读写流中的数据。 

    (2)Google的蓝牙聊天源码的分析

    ConnectThread:主动发起蓝牙连接线程,此线程在试图与设备进行传出连接时运行,它直接通过连接成功或失败

    ConnectedThread:蓝牙连接成功后启动,此线程在与远程设备连接期间运行。它处理所有传入和传出的传输

    AcceptThread:监听来自其他设备的蓝牙连接,此线程在侦听传入连接时运行,它一直运行到连接被接收(或者直到被取消)

    关键代码

    @SuppressLint("NewApi") public class ChatService { private static final String TAG = "ChatService"; private static final String NAME_SECURE = "Bluetooth Secure"; private static final UUID UUID_ANDROID_DEVICE = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66"); private static final UUID UUID_OTHER_DEVICE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");//蓝牙串口服务 private final BluetoothAdapter mAdapter; private final Handler mHandler; private AcceptThread mSecureAcceptThread; private ConnectThread mConnectThread; private ConnectedThread mConnectedThread; private int mState; Context context; /** * 准备一个新的bluetoothchat会话 * * @param context * @param handler * @param mAdapter */ public ChatService(Context context, Handler handler, BluetoothAdapter mAdapter) { this.mAdapter = mAdapter; mState = ChatState.STATE_NONE; mHandler = handler; this.context = context; } /** * 设置聊天连接的当前状态 * * @param state */ private synchronized void setState(int state) { Log.d(TAG, "setState() " + mState + " -> " + state); mHandler.obtainMessage(ChatState.MESSAGE_STATE_CHANGE, state, mState).sendToTarget(); mState = state; } /** * 返回当前连接状态 * * @return */ public synchronized int getState() { return mState; } /** * 开始聊天 */ public synchronized void start() { /** * 取消试图连接的任何线程 */ if (mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; } /** *取消当前运行连接的任何线程 */ if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } setState(ChatState.STATE_LISTEN); /** * 启动线程监听一个bluetoothserversocket */ if (mSecureAcceptThread == null) { mSecureAcceptThread = new AcceptThread(); mSecureAcceptThread.start(); } } /** * 开始connectthread发起一个连接到一个远程设备 * * @param device 连接的蓝牙设备 */ public synchronized void connect(BluetoothDevice device) { /** * 取消试图连接的任何线程 */ if (mState == ChatState.STATE_CONNECTING) { if (mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; } } /** * 取消当前运行连接的任何线程 */ if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } /** *启动线程与给定设备连接 */ mConnectThread = new ConnectThread(device); mConnectThread.start(); setState(ChatState.STATE_CONNECTING); } /** * 开始connectedthread开始管理蓝牙连接 * * @param socket * @param device 已连接的蓝牙设备 */ public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) { Log.i(TAG, "connected"); /** * 取消完成连接的线程 */ if (mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; } /** * 取消当前运行连接的任何线程 */ if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } /** * 取消接受线程,因为我们只想连接到一个设备 */ if (mSecureAcceptThread != null) { mSecureAcceptThread.cancel(); mSecureAcceptThread = null; } /** * 启动线程管理连接并执行传输 */ mConnectedThread = new ConnectedThread(socket); mConnectedThread.start(); /** * 将连接的设备的名称返回到UI活动. */ Message msg = mHandler.obtainMessage(ChatState.MESSAGE_DEVICE_INFO); Bundle bundle = new Bundle(); bundle.putString(ChatState.DEVICE_NAME, device.getName()); bundle.putString(ChatState.DEVICE_ADDRESS, device.getAddress()); msg.setData(bundle); mHandler.sendMessage(msg); setState(ChatState.STATE_CONNECTED); } /** * 停止所有的线程 */ public synchronized void stop() { if (mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; } if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } if (mSecureAcceptThread != null) { mSecureAcceptThread.cancel(); mSecureAcceptThread.kill(); mSecureAcceptThread = null; } setState(ChatState.STATE_NONE); } /** * 写入 * * @param out */ public void write(byte[] out) { Log.e(TAG, "write string out=" + Bytes2HexString(out)); ConnectedThread r; synchronized (this) { if (mState != ChatState.STATE_CONNECTED) return; r = mConnectedThread; } r.write(out); } /** * byte[]转string */ public static String Bytes2HexString(byte[] b) { String ret = ""; for (int i = 0; i < b.length; i++) { String hex = Integer.toHexString(b[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } ret += hex.toUpperCase(); } return ret; } /** * 指示连接尝试失败,重新连接 */ private void connectionFailed() { ChatService.this.start(); } /** * 此线程在侦听传入连接时运行。它的行为像一个服务器端客户端。它一直运行到连接被接收(或者直到被取消) */ private class AcceptThread extends Thread { private BluetoothServerSocket mmServerSocket; boolean isRunning = true; private boolean mmss() { if (mmServerSocket == null) { return false; } return true; } public AcceptThread() { BluetoothServerSocket tmp = null; /** *创建一个新的BluetoothServerSocket监听服务器套接字 */ try { if (ChatState.DEVICE_ANDROID) tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_ANDROID_DEVICE); else tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_OTHER_DEVICE); } catch (IOException e) { try { throw new SocketException(context, "error-AcceptThread", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-AcceptThread"); } } mmServerSocket = tmp; } public void run() { setName("AcceptThread"); BluetoothSocket socket = null; Log.i(TAG, "run"); /** * 如果没有连接,监听服务器套接字 */ while (mState != ChatState.STATE_CONNECTED && isRunning) { try { /** * 这是一个阻塞调用,只会返回成功的连接或异常 */ if (mmss()) socket = mmServerSocket.accept(); } catch (IOException e) { try { throw new SocketException(context, "error-AcceptThread-run1", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-AcceptThread-run1"); } break; } /** * 如果一个连接被接受 */ if (socket != null) { synchronized (ChatService.this) { switch (mState) { case ChatState.STATE_LISTEN: case ChatState.STATE_CONNECTING: /** * 正常情况。启动连接的线程 */ connected(socket, socket.getRemoteDevice()); break; case ChatState.STATE_NONE: case ChatState.STATE_CONNECTED: /** * 要么没有准备好,要么已经连接。终止新套接字。 */ try { socket.close(); } catch (IOException e) { try { throw new SocketException(context, "error-AcceptThread-run2", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-AcceptThread-run2"); } } break; } } } } } /** * 关闭Socket */ public void cancel() { try { if (mmss()) { mmServerSocket.close(); } mmServerSocket = null; } catch (IOException e) { try { throw new SocketException(context, "error-cancel", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-cancel"); } } } public void kill() { isRunning = false; } } /** * 此线程在试图与设备进行传出连接时运行。它直接通过连接成功或失败 */ private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; private boolean mms() { if (mmSocket == null) { return false; } return true; } public ConnectThread(BluetoothDevice device) { mmDevice = device; BluetoothSocket tmp = null; /** * 得到一个与给定的蓝牙设备连接BluetoothSocket */ try { if (ChatState.DEVICE_ANDROID) tmp = device.createRfcommSocketToServiceRecord(UUID_ANDROID_DEVICE); else tmp = device.createRfcommSocketToServiceRecord(UUID_OTHER_DEVICE); } catch (IOException e) { try { throw new SocketException(context, "error-ConnectThread", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-ConnectThread"); } } mmSocket = tmp; } public void run() { /** * 关闭蓝牙扫描,因为它会减慢连接速度 */ mAdapter.cancelDiscovery(); /** * 连接BluetoothSocket */ try { /** * 这是一个阻塞调用,只会返回成功的连接或异常 */ if (mms()) mmSocket.connect(); } catch (IOException e) { cancel(); connectionFailed(); return; } /** * 用完复位 ConnectThread */ synchronized (ChatService.this) { mConnectThread = null; } connected(mmSocket, mmDevice); } /** * 关闭Socket */ public void cancel() { try { if (mms()) mmSocket.close(); } catch (IOException e) { try { throw new SocketException(context, "error-cancel", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-cancel"); } } } } /** * 此线程在与远程设备连接期间运行。它处理所有传入和传出的传输 * */ private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; private boolean mms() { if (mmSocket == null) { return false; } return true; } public ConnectedThread(BluetoothSocket socket) { mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; Log.i(TAG, "ConnectedThread"); /** * 得到BluetoothSocket的输入和输出流 */ try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { try { throw new SocketException(context, "error-ConnectedThread", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-ConnectedThread"); } } mmInStream = tmpIn; mmOutStream = tmpOut; Log.i(TAG, "ConnectedThread mmInStream=" + mmInStream); Log.i(TAG, "ConnectedThread mmOutStream=" + mmOutStream); } public void run() { byte[] buffer; ArrayList<Integer> arr_byte = new ArrayList<Integer>(); int before = 0; /** * 循环读取输入流 */ while (true) { try { int data = mmInStream.read(); if (data == 0x0D && before == 0x0A) { buffer = new byte[arr_byte.size()]; for (int i = 0; i < buffer.length; i++) { buffer[i] = arr_byte.get(i).byteValue(); } Log.i(TAG, "run arr_byte=" + arr_byte); Log.i(TAG, "run arr_byte.size()=" + arr_byte.size()); Log.i(TAG, "run buffer=" + buffer); Log.i(TAG, "run buffer=" + buffer.length); mHandler.obtainMessage(ChatState.MESSAGE_READ , buffer.length, -1, buffer).sendToTarget(); arr_byte = new ArrayList<Integer>(); } else { arr_byte.add(data); } before = data; } catch (IOException e) { connectionFailed(); break; } } } /** * 写入输出流 * * @param buffer */ public void write(byte[] buffer) { Log.i(TAG, "write buffer=" + buffer); Log.i(TAG, "write buffer.length=" + buffer.length); try { byte[] buffer2 = new byte[buffer.length + 2]; for (int i = 0; i < buffer.length; i++) buffer2[i] = buffer[i]; buffer2[buffer2.length - 2] = 0x0A; buffer2[buffer2.length - 1] = 0x0D; Log.i(TAG, "write buffer2.length=" + buffer2.length); mmOutStream.write(buffer2); Log.i(TAG, "write mmOutStream=" + mmOutStream); mHandler.obtainMessage(ChatState.MESSAGE_WRITE , -1, -1, buffer).sendToTarget(); } catch (IOException e) { try { throw new SocketException(context, "error-write", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-write"); } } } /** * 关闭Socket */ public void cancel() { try { if (mms()) mmSocket.close(); } catch (IOException e) { try { throw new SocketException(context, "error-cancel", e); } catch (SocketException e1) { e1.printStackTrace(); Log.e("soketException", "error-cancel"); } } } } }

    (2)在ChatConnectControl负责接收Handler消息进行处理,提供若干函数供UI使用。

    public class ChatConnectControl { public static String TAG = "ChatConnectControl"; /** * 蓝牙会话监听 */ private BluetoothChatListener mBluetoothChatListener = null; /** * 蓝牙默认的适配器 */ private BluetoothAdapter mBluetoothAdapter = null; /** * 蓝牙聊天服务 */ private ChatService mChatService = null; private String mDeviceName = null; private String mDeviceAddress = null; /** * 是否已连接 */ private boolean isConnected = false; /** * 是否福连接中 */ private boolean isConnecting = false; private Context mContext; public ChatConnectControl(Context context, BluetoothAdapter mBluetoothAdapter) { mContext = context; this.mBluetoothAdapter = mBluetoothAdapter; } /** * 服务是否启动 * * @return */ public boolean isServiceAvailable() { return mChatService != null; } /** * 初始化蓝牙聊天服务 */ public void setupService() { mChatService = new ChatService(mContext, mHandler, mBluetoothAdapter); } /** * 蓝牙聊天当前连接状态 * * @return */ public int getServiceState() { if (mChatService != null) return mChatService.getState(); else return -1; } /** * 启动 */ public void startService() { if (mChatService != null) { if (mChatService.getState() == ChatState.STATE_NONE) { mChatService.start(); } } } /** * 停止 */ public void stopService() { if (mChatService != null) { mChatService.stop(); } new Handler().postDelayed(new Runnable() { public void run() { if (mChatService != null) { mChatService.stop(); } } }, 500); } /** * 重启/切换 */ public void setDeviceTarget() { stopService(); startService(); } /** * 连接 * * @param address */ public void connect(String address) { Log.e(TAG, "connect address=" + address); BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); mChatService.connect(device); } /** * 断开 */ public void disconnect() { Log.i(TAG, "disconnect"); if (mChatService != null) { mChatService.stop(); if (mChatService.getState() == ChatState.STATE_NONE) { mChatService.start(); } } } /** * 发送 * * @param data */ public void send(String data) { if (mChatService.getState() == ChatState.STATE_CONNECTED) { Log.i(TAG, "send data.getBytes()=" + data.getBytes()); mChatService.write(data.getBytes()); } } /** * 配置监听 * * @param listener */ public void setBluetoothChatListener(BluetoothChatListener listener) { mBluetoothChatListener = listener; } /** * 处理Handler接收的内容 */ @SuppressLint("HandlerLeak") private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case ChatState.MESSAGE_WRITE: byte[] readBuf_write = (byte[]) msg.obj; Log.i(TAG, "MESSAGE_READ readBuf_write=" + readBuf_write); Log.i(TAG, "MESSAGE_READ readBuf_write.length=" + readBuf_write.length); Log.i(TAG, "MESSAGE_WRITE"); break; case ChatState.MESSAGE_READ: Log.i(TAG, "MESSAGE_READ"); byte[] readBuf = (byte[]) msg.obj; String readMessage = new String(readBuf); Log.i(TAG, "MESSAGE_READ readBuf=" + readBuf); Log.i(TAG, "MESSAGE_READ readBuf.length=" + readBuf.length); Log.i(TAG, "MESSAGE_READ readMessage=" + readMessage); Log.i(TAG, "MESSAGE_READ readMessage.length=" + readMessage.length()); if (readBuf != null && readBuf.length > 0) { if (mBluetoothChatListener != null) { Log.i(TAG, "MESSAGE_READ onDataReceived"); mBluetoothChatListener.onDataReceived(readBuf, readMessage); } } break; case ChatState.MESSAGE_DEVICE_INFO: mDeviceName = msg.getData().getString(ChatState.DEVICE_NAME); mDeviceAddress = msg.getData().getString(ChatState.DEVICE_ADDRESS); if (mBluetoothChatListener != null) mBluetoothChatListener.onDeviceConnected(mDeviceName, mDeviceAddress); Log.i(TAG, "MESSAGE_DEVICE_NAME" + mDeviceName); Log.i(TAG, "MESSAGE_DEVICE_NAME" + mDeviceAddress); isConnected = true; break; case ChatState.MESSAGE_STATE_CHANGE: Log.i(TAG, "MESSAGE_STATE_CHANGE:" + msg.arg2 + "->" + msg.arg1); if (mBluetoothChatListener != null) mBluetoothChatListener.onServiceStateChanged(msg.arg2 + "->" + msg.arg1); if (isConnected && msg.arg1 != ChatState.STATE_CONNECTED) { if (mBluetoothChatListener != null) mBluetoothChatListener.onDeviceDisconnected(); isConnected = false; mDeviceName = null; mDeviceAddress = null; } if (!isConnecting && msg.arg1 == ChatState.STATE_CONNECTING) { isConnecting = true; } else if (isConnecting) { if (msg.arg1 != ChatState.STATE_CONNECTED) { if (mBluetoothChatListener != null) mBluetoothChatListener.onDeviceConnectionFailed(); } isConnecting = false; } break; } } }; }(3)配置蓝牙聊天监听接口回调供UI展示 public interface BluetoothChatListener { /** * 设备连接中 * * @param name * @param address */ void onDeviceConnected(String name, String address); /** * 设备断开连接 */ void onDeviceDisconnected(); /** * 设备连接失败 */ void onDeviceConnectionFailed(); /** * 聊天状态 * * @param state */ void onServiceStateChanged(String state); /** * 接收消息 * * @param data * @param message */ void onDataReceived(byte[] data, String message); }

    小结

    剩余UI部分这里就不贴出来了,项目已经上传至GitHub,项目包含经典蓝牙与BLE以及ibeacon,请自行阅读需要的模块,经典蓝牙模块基本实现从蓝牙配对到单聊的功能。

    ①关于配对在Android4.4以上(Android6.0(小米4)、Android5.1(魅蓝2)\、Andro4id.4.4.2(vivo))测试可用且不弹配对框,但是在Android4.2.2(oppo)还是会弹配对框......

    ②聊天传输符号有个别不识别,在Android4.2.2(oppo)接收表情不识别......

    以上便关于经典蓝牙的主要内容,如果有任何不妥之处,还请各位指出。

    转载请注明原文地址: https://ju.6miu.com/read-149606.html

    最新回复(0)