前面介绍了蓝牙的一些知识,今天来聊一聊蓝牙之间的通讯,实现两个设备通讯。java
我用两部手机实现相互发消息的功能android
不管是BluetoothSocket,仍是BluetoothServerSocket,都须要一个UUID(全局惟一标识符,UniversallyUnique Identifier)git
蓝牙的UUIDgithub
两个蓝牙设备进行链接时须要使用同一个UUID。但不少读者可能发现,有不少型号的手机(多是非Android系统的手机)之间使用了不一样的程序也能够使用蓝牙进行通信。从表面上看,它们之间几乎不可能使用同一个UUID。数组
UUID的格式以下:网络
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxsocket
UUID的格式被分红5段,其中中间3段的字符数相同,都是4,第1段是8个字符,最后一段是12个字符。因此UUID其实是一个8-4-4-4-12的字符串。ide
实际上,UUID和TCP的端口同样,也有一些默认的值。例如,将蓝牙模拟成串口的服务就使用了一个标准的UUID:this
00001101-0000-1000-8000-00805F9B34FBspa
除此以外,还有不少标准的UUID,以下面就是两个标准的UUID:
信息同步服务:00001104-0000-1000-8000-00805F9B34FB
文件传输服务:00001106-0000-1000-8000-00805F9B34FB
蓝牙终端间数据传输
经过蓝牙传输数据与Socket相似。在网络中使用Socket和ServerSocket控制客户端和服务端的数据读写。而蓝牙通信也由客户端和服务端Socket来完成。蓝牙客户端Socket是BluetoothSocket,蓝牙服务端Socket是BluetoothServerSocket。这两个类都在android.bluetooth包中。
不管是BluetoothSocket,仍是BluetoothServerSocket,都须要一个UUID(全局惟一标识符,Universally Unique Identifier),UUID至关于Socket的端口,而蓝牙地址至关于Socket的IP。
模拟一个蓝牙数据的传输:
private ListView lvDevices; // 获取到蓝牙适配器 private BluetoothAdapter mBluetoothAdapter; // ListView的字符串数组适配器 private List<String> bluetoothDevices = new ArrayList<String>(); private ArrayAdapter<String> arrayAdapter; // UUID,蓝牙创建连接须要的 private final UUID MY_UUID = UUID .fromString("abcd1234-ab12-ab12-ab12-abcdef123456");//随便定义一个 // 获取到选中设备的客户端串口,全局变量,不然链接在方法执行完就结束了 private BluetoothSocket clientSocket; // 选中发送数据的蓝牙设备,全局变量,不然链接在方法执行完就结束了 private BluetoothDevice device; // 获取到向设备写的输出流,全局变量,不然链接在方法执行完就结束了 private OutputStream os;//输出流 // 为其连接建立一个名称 private final String NAME = "Bluetooth_Socket"; // 服务端利用线程不断接受客户端信息 private AcceptThread thread;
主要代码:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.demo2); initView(); } private void initView() { findViewById(R.id.btn1).setOnClickListener(this); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); lvDevices = (ListView) findViewById(R.id.listview); //获取已经配对的蓝牙设备 Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { bluetoothDevices.add(device.getName() + ":"+ device.getAddress()); } } arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1,bluetoothDevices); lvDevices.setAdapter(arrayAdapter); lvDevices.setOnItemClickListener(this);//Activity实现OnItemClickListener接口 //每搜索到一个设备就会发送一个该广播 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(receiver, filter); //当所有搜索完后发送该广播 filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(receiver, filter); // 实例接收客户端传过来的数据线程 thread = new AcceptThread(); // 线程开始 thread.start(); } @Override public void onClick(View v) { //若是当前在搜索,就先取消搜索 if (mBluetoothAdapter.isDiscovering()) { mBluetoothAdapter.cancelDiscovery(); } //开启搜索 mBluetoothAdapter.startDiscovery(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String s = arrayAdapter.getItem(position); String address = s.substring(s.indexOf(":") + 1).trim();//把地址解析出来 //主动链接蓝牙服务端 try { //判断当前是否正在搜索 if (mBluetoothAdapter.isDiscovering()) { mBluetoothAdapter.cancelDiscovery(); } try { if (device == null) { //得到远程设备 device = mBluetoothAdapter.getRemoteDevice(address); } if (clientSocket == null) { //建立客户端蓝牙Socket clientSocket = device.createRfcommSocketToServiceRecord(MY_UUID); //开始链接蓝牙,若是没有配对则弹出对话框提示咱们进行配对 clientSocket.connect(); //得到输出流(客户端指向服务端输出文本) os = clientSocket.getOutputStream(); } } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "失败", Toast.LENGTH_LONG).show(); } if (os != null) { //往服务端写信息 os.write("蓝牙信息来了".getBytes("utf-8")); // 吐司一下,告诉用户发送成功 Toast.makeText(this, "发送信息成功,请查收", Toast.LENGTH_LONG).show(); } } catch (Exception e) { e.printStackTrace(); // 若是发生异常则告诉用户发送失败 Toast.makeText(this, "发送信息失败", Toast.LENGTH_LONG).show(); } } /** * 定义广播接收器 */ private final 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) { bluetoothDevices.add(device.getName() + ":" + device.getAddress()); arrayAdapter.notifyDataSetChanged();//更新适配器 } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //已搜素完成 Toast.makeText(Demo2Activity.this,"已搜索完成",Toast.LENGTH_LONG).show(); } } }; // 建立handler,由于咱们接收是采用线程来接收的,在线程中没法操做UI,因此须要handler Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); // 经过msg传递过来的信息,吐司一下收到的信息 Log.i(NAME,msg.obj.toString());// 接收其余设备传过来的消息 Toast.makeText(Demo2Activity.this, (String) msg.obj, Toast.LENGTH_LONG).show(); } }; // 服务端接收信息线程 private class AcceptThread extends Thread { private BluetoothServerSocket serverSocket;// 服务端接口 private BluetoothSocket socket;// 获取到客户端的接口 private InputStream is;// 获取到输入流 private OutputStream os;// 获取到输出流 public AcceptThread() { try { // 经过UUID监听请求,而后获取到对应的服务端接口 serverSocket = mBluetoothAdapter .listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (Exception e) { // TODO: handle exception } } public void run() { try { // 接收其客户端的接口 socket = serverSocket.accept(); // 获取到输入流 is = socket.getInputStream(); // 获取到输出流 os = socket.getOutputStream(); // 无线循环来接收数据 while (true) { // 建立一个128字节的缓冲 byte[] buffer = new byte[128]; // 每次读取128字节,并保存其读取的角标 int count = is.read(buffer); // 建立Message类,向handler发送数据 Message msg = new Message(); // 发送一个String的数据,让他向上转型为obj类型 msg.obj = new String(buffer, 0, count, "utf-8"); // 发送数据 handler.sendMessage(msg); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }
分别在两个手机上安装APP,点击扫描设备,而后点击链接的蓝牙名称发送消息
下图是我发送给另外一个设备,而后另外一个收到消息在发送消息过来。
效果图: