蓝牙设备连接 蓝牙的连接过程
获取->配对->连接
权限 首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。
1 <uses-permissionandroid:name="android.permission.BLUETOOTH" />
允许程序连接到已配对的蓝牙设备。
1 <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
获取可用蓝牙设备 引包 操作蓝牙主要用到的类 BluetoothAdapter类,使用时导包
1 import android.bluetooth.BluetoothAdapter;
BluetoothAdapter 代表本地设备的蓝牙适配器。
该BluetoothAdapter可以执行基本的蓝牙任务,例如启动设备发现,查询配对的设备列表,使用已知的MAC地址实例化一个BluetoothDevice类,并创建一个BluetoothServerSocket监听来自其他设备的连接请求
获取蓝牙适配器 1 BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
判断蓝牙是否开启 并启动蓝牙 1 2 3 4 5 6 7 if (!mBluetoothAdapter.isEnabled()){ Intent enabler = new Intent (BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enabler, REQUEST_ENABLE); }
获取本地蓝牙信息和已配对设备 连接中的设备不能在搜索回调中获取
只能在以配对设备中获取
1 2 3 4 5 6 7 8 9 10 11 12 String name = mBluetoothAdapter.getName(); String address = mBluetoothAdapter.getAddress(); Log.d(TAG,"bluetooth name =" +name+" address =" +address); Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices(); Log.d(TAG, "bonded device size =" +devices.size()); for (BluetoothDevice bonddevice:devices){ Log.d(TAG, "bonded device name =" +bonddevice.getName()+" address" +bonddevice.getAddress()); }
搜索设备 1 mBluetoothAdapter.startDiscovery();
搜索蓝牙设备,该过程是异步的,通过下面注册广播接受者,可以监听是否搜到设备。
1 2 3 4 5 6 7 8 IntentFilter filter = new IntentFilter (); filter.addAction(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mBluetoothReceiver, filter);
监听扫描结果
通过广播接收者查看扫描到的蓝牙设备,每扫描到一个设备,系统都会发送此广播(BluetoothDevice.ACTION_FOUNDE)。
其中参数intent可以获取蓝牙设备BluetoothDevice。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver (){ @Override public void onReceive (Context context, Intent intent) { String action = intent.getAction(); Log.d(TAG,"mBluetoothReceiver action =" +action); if (BluetoothDevice.ACTION_FOUND.equals(action)){ BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (scanDevice == null || scanDevice.getName() == null ) return ; Log.d(TAG, "name=" +scanDevice.getName()+"address=" +scanDevice.getAddress()); String name = scanDevice.getName(); if (name != null && name.equals(BLUETOOTH_NAME)){ mBluetoothAdapter.cancelDiscovery(); } }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){ } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); switch (device.getBondState()) { case BluetoothDevice.BOND_BONDING: Log.d("BlueToothTestActivity" , "正在配对......" ); break ; case BluetoothDevice.BOND_BONDED: Log.d("BlueToothTestActivity" , "完成配对" ); break ; case BluetoothDevice.BOND_NONE: Log.d("BlueToothTestActivity" , "取消配对" ); default : break ; } } } };
停止搜索 1 mBluetoothAdapter.cancelDiscovery();
设置蓝牙可见性 有时候扫描不到某设备,这是因为该设备对外不可见或者距离远,需要设备该蓝牙可见,这样该才能被搜索到。
可见时间默认值为120s,最多可设置300。
1 2 3 4 5 6 7 8 9 10 if (mBluetoothAdapter.isEnabled()) { if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent discoverableIntent = new Intent ( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120 ); startActivity(discoverableIntent); } }
配对 1 2 3 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { btDev.createBond(); }
取消配对 1 2 3 4 5 6 static public boolean removeBond (BluetoothDevice btDevice) throws Exception { Method removeBondMethod = btDevice.getClass().getMethod("removeBond" ); Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice); return returnValue.booleanValue(); }
服务端 android 蓝牙之间可以通过SDP协议建立连接进行通信,通信方式类似于平常使用socket。
首先创建BluetoothServerSocket ,BluetoothAdapter中提供了两种创建BluetoothServerSocket 方式,如下图所示为创建安全的RFCOMM Bluetooth socket,该连接是安全的需要进行配对。而通过listenUsingInsecureRfcommWithServiceRecord创建的RFCOMM Bluetooth socket是不安全的,连接时不需要进行配对。
其中的uuid需要服务器端和客户端进行统一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 private class AcceptThread extends Thread { private final BluetoothServerSocket mServerSocket; public AcceptThread () { BluetoothServerSocket tmp = null ; try { tmp = mAdapter.listenUsingRfcommWithServiceRecord( SERVICE_NAME, SERVICE_UUID); } catch (IOException e) { Log.e(TAG, "listen() failed" , e); } mServerSocket = tmp; } public void run () { BluetoothSocket socket = null ; while (mState != STATE_CONNECTED) { try { socket = mServerSocket.accept(); } catch (IOException e) { Log.e(TAG, "accept() failed" , e); break ; } if (socket != null ) { synchronized (BluetoothChatUtil.this ) { switch (mState) { case STATE_LISTEN: case STATE_CONNECTING: connected(socket, socket.getRemoteDevice()); break ; case STATE_NONE: case STATE_CONNECTED: try { socket.close(); } catch (IOException e) { Log.e(TAG, "Could not close unwanted socket" , e); } break ; } } } } if (D) Log.i(TAG, "END mAcceptThread" ); } public void cancel () { if (D) Log.d(TAG, "cancel " + this ); try { mServerSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of server failed" , e); } } }
客户端 客户端主要用来创建RFCOMM socket,并连接服务端。
先扫描周围的蓝牙设备,如果扫描到指定设备则进行连接。mBlthChatUtil.connect(scanDevice)连接到设备,
连接过程主要在ConnectThread线程中进行,先创建socket,方式有两种,
如下代码中是安全的(createRfcommSocketToServiceRecord)。另一种不安全连接对应的函数是createInsecureRfcommSocketToServiceRecord。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 private class ConnectThread extends Thread { private BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread (BluetoothDevice device) { mmDevice = device; BluetoothSocket tmp = null ; try { mmSocket = device.createRfcommSocketToServiceRecord (SERVICE_UUID); } catch (IOException e) { Log.e(TAG, "create() failed" , e); mmSocket = null ; } } public void run () { Log.i(TAG, "BEGIN mConnectThread" ); try { mmSocket.connect(); } catch (IOException e) { connectionFailed(); try { mmSocket.close(); } catch (IOException e2) { e2.printStackTrace(); } return ; } connected(mmSocket, mmDevice); } public void cancel () { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed" , e); } } }
数据传输 客户端与服务端连接成功后都会调用connected(mmSocket, mmDevice),创建一个ConnectedThread线程()。
该线程主要用来接收和发送数据。客户端和服务端处理方式一样。该线程通过socket获得输入输出流。
1 2 private InputStream mmInStream = socket.getInputStream();private OutputStream mmOutStream = socket.getOutputStream();
发送数据
1 2 3 4 5 6 7 8 9 10 public void write (byte [] buffer) { try { mmOutStream.write(buffer); mHandler.obtainMessage(MESSAGE_WRITE, -1 , -1 , buffer) .sendToTarget(); } catch (IOException e) { Log.e(TAG, "Exception during write" , e); } }
接收数据
线程循环进行接收数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void run () { while (true ) { try { byte [] buffer = new byte [1024 ]; int bytes = mmInStream.read(buffer); Message msg = mHandler.obtainMessage(MESSAGE_READ); Bundle bundle = new Bundle (); bundle.putByteArray(READ_MSG, buffer); msg.setData(bundle); mHandler.sendMessage(msg); } catch (IOException e) { Log.e(TAG, "disconnected" , e); connectionLost(); break ; } } }