通过蓝牙API可以实现的功能:
1.扫描其他蓝牙设备
2.为匹配设备查找本地的蓝牙适配器
3.建立 RFCOMM 通道
4.连接设备
5.管理多连接
6.和其他蓝牙设备交换数据
所有蓝牙的API都在 android.bluetooth包下。
BluetoothAdapter:
通过这个类可以找到其他蓝牙设备,用一个已知的MAC地址实例化一个 BluetoothDevice,并且创建一个 BluetoothServerSocket去监听其他设备的交互。
BluetoothDevice:
使用这个类可以通过 BluetoothSocket向远程设备发起连接请求,或者查询设备信息
BluetoothSocket:
类似于Java的Socket,允许应用和蓝牙设备交换数据。
BluetoothServerSocket:
类似于Java的ServerSocket,监听请求。为了建立蓝牙连接,一个设备需要通过这个类的方法去建立一个Server。
BluetoothClass:
描述了一个蓝牙设备的基础特性和功能。
BluetoothProfile:
一个表示蓝牙配置文件的接口。
BluetoothHeadset:
为手机使用的蓝牙耳机提供支持。
BluetoothA2dp:
定义高品质的音频如何通过蓝牙连接从一个设备传输到另一个设备。”A2DP“是一个协议的名称(Advanced Audio Distribution Profile)
BluetoothHealth:
表示一个Health Device Profile代理,它控制蓝牙服务。
BluetoothHealthCallback:
用来实现BluetoothHealth回调的抽象类,你必须扩展这个类并实现回调函数方法来接收应用程序的注册状态改变以及蓝牙串口状态的更新。
BluetoothHealthAppConfiguration:
表示一个应用程序配置,Bluetooth Health第三方应用程序注册和一个远程Bluetooth Health设备通信。
BluetoothProfile.ServiceListener:
一个接口,当BluetoothProfile IPC客户端从服务器上建立连接或断开连接时,它负责通知它们
蓝牙的权限:
android.permission.BLUETOOTH:允许应用连接匹配的蓝牙设备
android.permission.BLUETOOTH_ADMIN:允许应用搜索并连接蓝牙设备
建立蓝牙的步骤:
和蓝牙设备建立连接的过程应该是:开启蓝牙-》搜索设备-》匹配设备-》建立连接。
开启蓝牙:
先要确定设备是否支持蓝牙,如果设备支持蓝牙,但是没有打开,可以在应用中提示用户开启。
以上步骤需要用到BluetoothAdapter。
获取BluetoothAdapter 实例的方法:
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();如果返回null,说明设备不支持蓝牙。如果没有打开蓝牙,提示用户开启:
if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }
这时会出现一个窗口让用户选择
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_ENABLE_BT: if (resultCode == RESULT_OK) { Toast.makeText(MainActivity.this, "蓝牙启用成功", Toast.LENGTH_SHORT).show(); } else if (resultCode == RESULT_CANCELED) { Toast.makeText(MainActivity.this, "蓝牙启用失败", Toast.LENGTH_SHORT).show(); } break; } }可以监听 BluetoothAdapter.ACTION_STATE_CHANGED的广播来检查蓝牙状态的改变,广播包含了EXTRA_PREVIOUS_STATE和EXTRA_STATE这2个extra。
extra的值包含以下几种常量:STATE_TURNING_ON:Bluetooth adapter正在开启, STATE_ON:Bluetooth adapter打开或者正在使用, STATE_TURNING_OFF:Bluetooth adapter正在关闭, STATE_OFF:Bluetooth adapter已经关闭。
IntentFilter filter2 = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mStateReceiver, filter2);
private final BroadcastReceiver mStateReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { // STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_OFF // BluetoothAdapter.EXTRA_STATE int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0); if (state == BluetoothAdapter.STATE_ON) { Toast.makeText(MainActivity.this, "STATE_ON", Toast.LENGTH_SHORT).show(); } else if (state == BluetoothAdapter.STATE_TURNING_ON) { Toast.makeText(MainActivity.this, "STATE_TURNING_ON", Toast.LENGTH_SHORT).show(); } } } };
搜索设备可以进行设备探测和查询已经匹配的设备。
匹配(paired )和连接(connected)的区别:
匹配:2个设备互相知道对方的存在,有一个共享的连接key用于验证,并且能够和对方建立一个加密的连接。
连接:2个设备当前共享一个 RFCOMM 通道并且可以互相交换数据。
当前API需要设备先进行匹配,才能进行 RFCOMM 连接。
查询已经匹配的设备:
private void queryPairedDevices() { Set<BluetoothDevice> pairedDevices = mBluetoothAdapter .getBondedDevices(); ArrayList<String> deviceInfoList = new ArrayList<String>(); if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { deviceInfoList.add(device.getName() + "\n" + device.getAddress()); } } getListFragment().updateDeviceList(deviceInfoList); }
设备探测
设备探测会扫描附近区域内的蓝牙设备,前提是其他蓝牙设备本身允许被探测到。Android设备默认是不可探测的,如果需要改变可探测性,可以通过系统设置或者在应用中提示用户让设备变得可以被探测到。
如果需要启动探测,搜索其他设备,调用 startDiscovery().,这个方法是异步的,调用后会立即返回一个布尔值,表明是否成功开始探测,探测进程通常会进行一个12秒左右的扫描:
mBluetoothAdapter.startDiscovery();可以注册广播接收到发现新设备的消息。
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter);
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); getListFragment().updateDeviceList(device); } } };
设置设备可以被探测:
设备默认在120秒内是可探测的,你可以为intent添加叫EXTRA_DISCOVERABLE_DURATION的extra来指定不同的时间。最长的时间限制是 3600秒,如果这个值是0的话,这个设备则会一直被探测到。任何小于0或者大于3600的值都会被自动设置为120。
private void enableDiscoverbility() { Intent discoverableIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverableIntent); }
调用以上代码后,会出现一个对话框提示:
如果想监听可被探测状态的改变,可以注册广播监听,广播的Action为BluetoothAdapter.ACTION_SCAN_MODE_CHANGED,这个广播包含2个Extra: EXTRA_SCAN_MODE 和EXTRA_PREVIOUS_SCAN_MODE,每个都对应3个状态常量: SCAN_MODE_CONNECTABLE_DISCOVERABLE(可连接可探测), SCAN_MODE_CONNECTABLE(不可被探测,但是可以和之前连接到这个设备的其他设备建立连接), or SCAN_MODE_NONE(既不能探测也不能连接)。
点击对话框的“允许”之后,其他设备用startDiscovery()可以探测到这台手机。根据Receiver中获取到的BluetoothDevice对象,可以取得手机详细信息。
连接设备:
为了建立2个设备间的连接,必须同时实现客户端和服务端。当服务端和客户端在同一个RFCOMM频道上建立一个 BluetoothSocket连接时,他们就可以进行通信。
在建立连接之前,如果2个设备之前没匹配过,系统会自动显示一个提示对话框,询问是否匹配。
蓝牙通信时,和Socket类似,包含客户端和服务端。服务端建立请求监听客户端的连接请求,客户端通过服务端的Mac地址来请求连接。
客户端和服务端通信是通过建立连接后,获取一个BluetoothSocket对象来完成的,BluetoothSocket对象可以获取输入输出流。
客户端和服务端获取BluetoothSocket的方式不同,作为服务端,首先需要监听客户端的请求。
1.通过BluetoothAdapter的listenUsingRfcommWithServiceRecord(String name, UUID uuid)方法获取BluetoothServerSocket对象。
2.通过调用BluetoothServerSocket对象的accept()方法监听客户端连接请求,这个accept方法是阻塞的,直到接收到客户端的连接请求,所以这个操作需要放在单独的线程中。
3.如果需要停止对连接请求的监听,调用BluetoothServerSocket的close()方法。
private class AcceptThread extends Thread { // The local server 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; } public void run() { if (D) Log.d(TAG, "Socket Type: " + mSocketType + "BEGIN mAcceptThread" + this); setName("AcceptThread" + mSocketType); BluetoothSocket socket = null; // 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; } } } } if (D) Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType); } public void cancel() { if (D) Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this); try { mmServerSocket.close(); } catch (IOException e) { Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e); } } }连接建立后,用accept()返回的BluetoothSocket对象进行通讯:
mConnectedThread = new ConnectedThread(socket, socketType); mConnectedThread.start();ConnectedThread:
private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket, String socketType) { 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) { Log.e(TAG, "temp sockets not created", e); } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { Log.i(TAG, "BEGIN mConnectedThread"); byte[] buffer = new byte[1024]; int bytes; // Keep listening to the InputStream while connected while (true) { try { // Read from the InputStream bytes = mmInStream.read(buffer); // Send the obtained bytes to the UI Activity mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer).sendToTarget(); } catch (IOException e) { Log.e(TAG, "disconnected", e); connectionLost(); // Start the service over to restart listening mode BluetoothChatService.this.start(); break; } } } /** * Write to the connected OutStream. * * @param buffer * The bytes to write */ public void write(byte[] buffer) { try { mmOutStream.write(buffer); // Share the sent message back to the UI Activity mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer).sendToTarget(); } catch (IOException e) { Log.e(TAG, "Exception during write", e); } } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed", e); } } } }以上代码通过BluetoothSocket的getInputStream()和getOutputStream()方法获取了输入输出流进行通讯,注意mmInStream.read(buffer)和mmOutStream.write(buffer)都是阻塞方法。
以上是蓝牙服务端的监听-连接-通信流程,下面看下客户端的连接-通信过程:
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 = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE); } else { tmp = device.createInsecureRfcommSocketToServiceRecord(MY_UUID_INSECURE); } } catch (IOException e) { Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e); } mmSocket = tmp; } public void run() { Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType); setName("ConnectThread" + mSocketType); // Always cancel discovery because it will slow down a connection mAdapter.cancelDiscovery(); // 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) { Log.e(TAG, "unable to close() " + mSocketType + " socket during connection failure", e2); } 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) { Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e); } } }客户端获取BluetoothSocket 对象的方法是通过BluetoothDevice的createRfcommSocketToServiceRecord(UUID uuid)获取的,这个BluetoothDevice对象是通过BluetoothAdapter的getRemoteDevice(String address)方法得到的,这个方法通过传入服务端的mac地址,可以获取到代表服务端设备的BluetoothDevice对象。
调用 BluetoothSocket 对象的connect()方法进行建立连接,这个方法是阻塞的。连接成功后,和服务端一样,通过BluetoothSocket 获取输入输出流进行通信。注意在调用connect方法前,如果还在进行设备搜索的操作,需要调用cancelDiscovery,否则会减缓连接速度。
DEMO下载地址:
http://download.csdn.net/detail/ohehehou/8387877