Android蓝牙高级操作(多连接)

来源:互联网 发布:web视频会议系统源码 编辑:程序博客网 时间:2024/05/18 20:08

  最近一直在做与bluetooth相关的应用。主要涉及android手机蓝牙的多连接问题。网上几乎没有与蓝牙多连接相关的具体实现资料,所以我开始从android的官方文档入手,大半夜的一个人坐下面看那英文文档,真TMD不是滋味,现在回想下当年做的英语阅读理解真似一坨shit。不过功夫不负有心人,终于搞清楚了它的构架和通信模式。这里我先讲bluetooth的基本操作,然后再深入讲解它的多连接问题(大家期待的重头戏)。注意:我这里主要讲的是多连接的核心实现,至于蓝牙的一些基础操作,我只是简单的介绍。如果有不懂的可以参考其他资料。我也做了一个测试Demo,里面的代码基本参考的官方文档,若有疑问可以去官网上看看。我试过一次可以连接三个手机。当然这不一定是极限数据,因为设备有限。有条件的朋友可以修改下代码,做下压力测试。

Demo代码下载:http://download.csdn.net/detail/wangwang6233/7188881

官方文档:http://developer.android.com/guide/topics/connectivity/bluetooth.html

参考博客:http://zhouyunan2010.iteye.com/blog/1186021

流程:

(1)蓝牙的介绍,相关API使用说明,使用蓝牙的准备工作。

(2)蓝牙的开启和关闭。

(3)设置设备可被搜索。

(4)搜索设备及广播接收器的注册。

(5)蓝牙的配对。

(6)蓝牙的连接服务端和客户端

(7)蓝牙的多连接操作。

讲解:

(1)蓝牙的介绍,相关API使用说明,使用蓝牙的准备工作。

        蓝牙,是一种支持设备短距离通信(一般10m内)的无线电技术。理论上一个蓝牙设备可以连接7个蓝牙设备(我没试过,只是理论上)。首先可行性是没问题的。其他蓝牙信息我就不阐述了,大家问度娘吧!在android app上使用bluetooth时需在AndroidManifest.xml中加上权限:

<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" /> 
      <uses-permissionandroid:name="android.permission.BLUETOOTH" /> 

我简单说下相关的API类,和常用的方法。

BluetoothAdapter:顾名思义,蓝牙适配器,蓝牙的打开、关闭、搜索都和它有关BluetoothAdapter.getDefaultAdapter()获取。

BluetoothDevice:看名字就知道,这个类是描述一个蓝牙设备,从它可以获取蓝牙的地址和设备名getAddress(),getName()。并且蓝牙设备有三种状态BOND_BONDED、BOND_BONDING、BOND_NONE分别是已绑定,绑定中、未绑定。

BluetoothServerSocket:这是服务端,通过accept()返回BluetoothSocket。既然是Socket相信大家都再熟悉不过了吧!如果你不太清楚socket编程,那就先去看java基础吧!这里我也不能偏题。

BluetoothSocket:这是客户端,connect()与服务端进行连接。通过它回去输入输出流。

(2)蓝牙的开启和关闭。

打开蓝牙:

private void startBluetooth() {if (mBluetoothAdapter == null) {// 表明此手机不支持蓝牙Log.d(TAG, "device is not supported bluebooth");return;}if (!mBluetoothAdapter.isEnabled()) { // 蓝牙未开启,则开启蓝牙mBluetoothAdapter.enable();}}
关闭蓝牙:

if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {mBluetoothAdapter.disable();}
(3)设置设备可被搜索。
// 使本机蓝牙在300秒内可被搜索private void ensureDiscoverable() {if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);//设置被发现时间startActivity(discoverableIntent);}}
(4)搜索设备及广播接收器的注册。

记得在onCreate()中注册,在onDestory()中unregisterReceiver(searchDevices);这是android广播机制的基础,不懂的可以回去看看android广播基础了。

private void register2Broadcast() {IntentFilter intent = new IntentFilter();intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);registerReceiver(searchDevices, intent);}private BroadcastReceiver searchDevices = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubString action = intent.getAction();if (BluetoothDevice.ACTION_FOUND.equals(action)) {BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//addDevice2List(device); 获取BluetoothDevice后剩下的自己处理了 }}};
(5)蓝牙的配对。

这里我们用到的是java的反射机制。

private void paireDevice(BluetoothDevice device) {try {Method createBondMethod = BluetoothDevice.class.getMethod("createBond");createBondMethod.invoke(device);} catch (Exception e) {// TODO: handle exceptione.getStackTrace();}}
(6)蓝牙的连接服务端和客户端

服务端:这里我声明了四个BluetoothSocket。因为accept()是阻塞操作,一旦连接成功就会返回BluetoothSocket,然后继续等待下一个连接。如此下去我们就实现了多连接。UUID是作为服务端的标识。获得BluetoothSocket后,我们就可以进行相关的操作了,获取输入输出流。进行相互的通信。

private class AcceptThread extends Thread {private final BluetoothServerSocket mmServerSocket;public AcceptThread() {// Use a temporary object that is later assigned to mmServerSocket,// because mmServerSocket is finalBluetoothServerSocket tmp = null;try {// MY_UUID is the app's UUID string, also used by the client// codetmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("eric",UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));} catch (IOException e) {}mmServerSocket = tmp;}public void run() {// Keep listening until exception occurs or a socket is returnedwhile (true) {try {Log.d(TAG, "thread is start and accept");if (socket == null) {socket = mmServerSocket.accept();Log.d(TAG, "accept ok");// If a connection was acceptedif (socket != null) {// Do work to manage the connection (in a separate// thread)Log.d(TAG, "manageConnectedSocket");manageConnectedSocket(socket);//相关处理函数// break;}}if (socket_two == null) {Log.d(TAG, "wait two");socket_two = mmServerSocket.accept();Log.d(TAG, "accept ok");// If a connection was acceptedif (socket != null) {// Do work to manage the connection (in a separate// thread)Log.d(TAG, "manageConnectedSocket");manageConnectedSocket(socket_two);}}if (socket_three == null) {socket_three = mmServerSocket.accept();Log.d(TAG, "accept ok");// If a connection was acceptedif (socket != null) {// Do work to manage the connection (in a separate// thread)Log.d(TAG, "manageConnectedSocket");manageConnectedSocket(socket_three);}}if (socket_four == null) {socket_four = mmServerSocket.accept();Log.d(TAG, "accept ok");// If a connection was acceptedif (socket != null) {// Do work to manage the connection (in a separate// thread)Log.d(TAG, "manageConnectedSocket");manageConnectedSocket(socket_four);break;}}} catch (IOException e) {break;}}}/** Will cancel the listening socket, and cause the thread to finish */public void cancel() {try {mmServerSocket.close();} catch (IOException e) {}}private void manageConnectedSocket(BluetoothSocket socket) {if (socket!=null) {readThread read = new readThread(socket);read.start();} }}
客户端:其中的UUID要与服务端的UUID相对应。同理connect()也是阻塞操作。连接成功后即返回BluetoothSocket。
private class ConnectThread extends Thread {private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device) {// Use a temporary object that is later assigned to mmSocket,// because mmSocket is finalBluetoothSocket tmp = null;mmDevice = device;// Get a BluetoothSocket to connect with the given BluetoothDevicetry {// MY_UUID is the app's UUID string, also used by the server// codetmp = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));} catch (IOException e) {}mmSocket = tmp;}public void run() {// Cancel discovery because it will slow down the connectionmBluetoothAdapter.cancelDiscovery();try {// Connect the device through the socket. This will block// until it succeeds or throws an exceptionLog.d(TAG, "thread is start and connect");mmSocket.connect();Log.d(TAG, "connect ok");Message msg=mHandler.obtainMessage();msg.what=3;msg.sendToTarget();} catch (IOException connectException) {// Unable to connect; close the socket and get outLog.d(TAG, "connect throw expection");try {mmSocket.close();} catch (IOException closeException) {}return;}// Do work to manage the connection (in a separate thread)socket=mmSocket;if(socket!=null){manageConnectedSocket(socket);}}/** Will cancel an in-progress connection, and close the socket */public void cancel() {try {socket.close();} catch (IOException e) {}}private void manageConnectedSocket(BluetoothSocket socket) {readThread read = new readThread(socket)//开启读取数据线程;read.start();Log.d(TAG, "the read is start");}}
数据读入代码:这里我们另起一个线程,当有输入流时就进行获取。这里我们与Handler一起使用,这样就可以在界面及时更新数据。
// 读取数据private class readThread extends Thread {private BluetoothSocket socket;public readThread(BluetoothSocket socket) {this.socket = socket;}public void run() {byte[] buffer = new byte[1024];int bytes;InputStream mmInStream = null;try {mmInStream = this.socket.getInputStream();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}while (flag) {try {Log.d(TAG, "get inputstream");// Read from the InputStreamif ((bytes = mmInStream.read(buffer)) > 0) {byte[] buf_data = new byte[bytes];for (int i = 0; i < bytes; i++) {buf_data[i] = buffer[i];}String s = new String(buf_data);Log.d(TAG, s);Message msg = new Message();msg.what = 2;msg.obj = s;mHandler.sendMessage(msg);}} catch (IOException e) {try {mmInStream.close();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}break;}}}}
发送数据(写入输出流)

这里可能有人不懂为什么这么麻烦。我这写的是服务端群发数据。所有有连接的客户端都能收到。

private void sendMessage(){String s = ed_message.getText().toString()+"";if (socket == null) {Log.d(TAG, "socket is not connect");} else {try {OutputStream os = socket.getOutputStream();os.write(s.getBytes());Log.d(TAG, "write to outputstream success,socket");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if (socket_two == null) {Log.d(TAG, "socket_two is not connect");} else {try {OutputStream os = socket_two.getOutputStream();os.write(s.getBytes());Log.d(TAG, "write to outputstream success,socket_two");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if (socket_three == null) {Log.d(TAG, "socket_three is not connect");} else {try {OutputStream os = socket_three.getOutputStream();os.write(s.getBytes());Log.d(TAG, "write to outputstream success,socket_three");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if (socket_four == null) {Log.d(TAG, "socket_four is not connect");} else {try {OutputStream os = socket_four.getOutputStream();os.write(s.getBytes());Log.d(TAG, "write to outputstream success,socket_four");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
(7)蓝牙的多连接操作。

代码已在(6)中贴出来了,这里我就解释一下,这里所谓的多连接就是一个服务端和多个客户端。重点就是在服务端一个accept()成功获取BluetoothSocket后,我们要继续监听下一个BluetoothSocket,就像我为什么声明了四个BluetoothSocket(socket,socket_two,socket_three,socket_four);其实没有什么技巧,只要你熟悉socket编程。多试试就OK了。大家可以下载测试Demo进行多次调试。多看看就懂了,而且我的代码还有许多可以改进的地方,大家可以互相讨论学习,路总是人走出来了,如果总吃现成的就永远得不到创新。我这里仅仅做个启发。

Demo代码下载:http://download.csdn.net/detail/wangwang6233/7188881












3 0