一:简述
在上篇文章中,我们已经对传统蓝牙的开启以及周围设备的搜索有了一个初步的了解。
本篇文章将对蓝牙之间配对以及蓝牙的客户端和服务端建立连接的过程进行讲解。另外在上篇文章中虽然最后搜索到了周围的蓝牙设备,但并没有区分开来所有设备中的已配对和未配对的设备,这个缺陷将在本篇文章中改善。
二:搜索周围蓝牙设备(将已配对设备列表和未配对列表区分开来)
通过调用BluetoothAdapter的startDiscovery()方法即可开始扫描。
//点击搜索按钮,开始搜索周围设备 begin
mSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//如果正在扫描,则取消正在进行的扫描
if(bluetoothAdapter.isDiscovering()){
bluetoothAdapter.cancelDiscovery();
}
bluetoothDevicesunpaired.clear(); //未配对设备列表
bluetoothDevicespaired.clear(); //已配对设备列表
bluetoothAdapter.startDiscovery();
}
});
switch (action){
case BluetoothDevice.ACTION_FOUND:
int state=device.getBondState();
if(state==BluetoothDevice.BOND_BONDED){
paired_adapter.addDev(device);
}
else{
unpaired_adapter.addDev(device);
}
break;
}
二:蓝牙设备的配对
当本机搜索到周围设备之后,要先进行配对之后才能连接。先来看一下几个要用到的函数 :
BluetoothDevice.createBond(); //启动与远程设备的绑定(配对)过程。
BluetoothDevice.getBondState(); //获取远程设备的绑定状态。返回的值可能为: BOND_NONE,
BOND_BONDING, BOND_BONDED,分别表示未配对,正在配对中以
及已经配对这三种状态。
为未配对蓝牙列表设置点击事件,点击之后会开始配对,两台设备上都会弹出来请求配对的对话框,双方确认之后配对成功,之后该设备会从未配对列表转移到配对列表。
//为未配对蓝牙列表设置点击事件,我们暂定为点击一次之后,进行蓝牙配对
unpaired_adapter.setOnItemClickListener(new DeviceAdapter.OnItemClickListener() {
@Override
public void onClick(int position) {
Log.i("lcc check","the position is "+position);
bluetoothDevicesunpaired.get(position).createBond(); //创建绑定。执行之后会弹出来请求配对的对话框
}
});
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
int newState=device.getBondState();
if(newState==BluetoothDevice.BOND_BONDED){
Log.i("lcc check","the bond state is bonded");
unpaired_adapter.removeDev(device); //从未配对列表中删除
paired_adapter.addDev(device); //添加到已配对设备列表
Toast.makeText(MainActivity.this,"与设备"+device.getName()+"配对成功",Toast.LENGTH_LONG);
}
else if(newState==BluetoothDevice.BOND_BONDING){
Log.i("lcc check","the bond state is bonding");
}
else{
Log.i("lcc check","the bond state is nobond");
unpaired_adapter.addDev(device);
paired_adapter.removeDev(device);
Toast.makeText(MainActivity.this,"与设备"+device.getName()+"配对失败",Toast.LENGTH_LONG);
}
break;
三:传统蓝牙的连接
蓝牙之间通过BluetoothSocket和BluetoothServerSocket通信。这和socket通信很相似.
两个蓝牙设备之间通信,必须有一个作为客户端,另一个作为服务器端。
//以下是API文档上给出的解释
The interface for Bluetooth Sockets is similar to that of TCP sockets: Socket
and ServerSocket
. On the server side, use a BluetoothServerSocket
to create a listening server socket. When a connection is accepted by the BluetoothServerSocket
, it will return a new BluetoothSocket
to manage the connection. On the client side, use a single BluetoothSocket
to both initiate an outgoing connection and to manage the connection.
The most common type of Bluetooth socket is RFCOMM, which is the type supported by the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
在服务器端,使用BluetoothServerSocket
创建侦听服务器socket。当接受连接时BluetoothServerSocket
,它将返回一个新的BluetoothSocket
来管理连接。在客户端,使用单个BluetoothSocket
来启动传出连接和管理连接。最常见的蓝牙插座类型是RFCOMM,它是Android API支持的类型。RFCOMM是一种面向连接的蓝牙流媒体传输。它也称为串行端口配置文件(SPP)。
下面是客户端和服务器端创建连接的过程:(注:里面的SPP_UUID使我们自己定义的一个uuid号,创建RfcommSocket连接需要客户端和服务器使用确定的uuid号)。
//客户端
//为已配对蓝牙列表创建点击事件,暂且设置为:点击一次,与服务端创建连接 begin
paired_adapter.setOnItemClickListener(new DeviceAdapter.OnItemClickListener() {
@Override
public void onClick(int position) {
Log.i("lcc check","the position is "+position);
try {
bluetoothSocket=bluetoothDevicespaired.get(position).createInsecureRfcommSocketToServiceRecord(SPP_UUID);
Log.i("lcc check","客户端和服务器尝试创建连接");
if(bluetoothSocket.isConnected()){
//do nothing
Log.i("lcc check","连接已经建立,无需再进行连接");
}
else{
bluetoothSocket.connect();
}
if(bluetoothSocket.isConnected()){
Log.i("lcc check","客户端和服务器已连接");
Toast.makeText(MainActivity.this,"连接已经建立",Toast.LENGTH_LONG).show();
readThread=new ReadThread();
readThread.start();
/* Intent intent=new Intent(MainActivity.this,CommunicationActivity.class);
startActivity(intent);*/
}
}
catch (Exception e){
Log.i("lcc check","the connection has not created");
}
}
});
//为已配对蓝牙列表创建点击事件,暂且设置为:点击一次,与服务端创建连接 end
//服务器端
private class myThread extends Thread{
@Override
public void run(){
try{
bluetoothServerSocket=bluetoothAdapter.listenUsingRfcommWithServiceRecord("lcc",SPP_UUID);
}
catch (Exception e){
e.printStackTrace();
}
Log.i("lcc check","等待连接...");
while(true) {
try{
socket=bluetoothServerSocket.accept();
BluetoothDevice device=socket.getRemoteDevice();
Log.i("lcc check","客户端名字为:"+device.getName()+device.getAddress());
if(socket.isConnected()){
Log.i("lcc check","已经建立连接");
Message message=Message.obtain();
message.what=CONNECTED;
myhandler.sendMessage(message);
readThread=new ReadThread();
readThread.start();
}
else{
Message message=Message.obtain();
message.what=DISCONNECTED;
myhandler.sendMessage(message);
}
}
catch (Exception e){
Log.i("lcc check","未能成功建立连接");
e.printStackTrace();
Message message=Message.obtain();
message.what=DISCONNECTED;
myhandler.sendMessage(message);
}
}
}
}
四:客户端和服务器之间通信(传递短消息)
当创建连接之后,两者之间就可以通过Bluetoothsocket收发消息了,代码如下:
//客户端发
//通过socket发送信息 begin
private void sendMessage(String msg) {
if (bluetoothSocket == null) {
//showToast("没有连接");
Toast.makeText(MainActivity.this,"没有连接",Toast.LENGTH_SHORT);
return;
}
try {
Log.i("lcc check","the msg is "+msg);
//showToast("发出的指令是" + msg);
Toast.makeText(MainActivity.this,"客户端:"+msg,Toast.LENGTH_SHORT).show();
//输出流输出信息
OutputStream os = bluetoothSocket.getOutputStream();
os.write(msg.getBytes());
os.flush();
String info=String.format("\n客户端:%s",msg);
communicationMsg.append(info);
} catch (IOException e) {
e.printStackTrace();
}
}
//通过socket发送信息 end
//服务器端收
private class ReadThread extends Thread {
public void run() {
byte[] buffer = new byte[1024];
int bytes;
InputStream is = null;
try {
is = socket.getInputStream();
while (true) {
if ((bytes = is.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.i("lcc check","the msg is "+s);
/*
在子线程里面修改组件等无效,会报错
String info=String.format("\n客户端:%s",s);
communication.append(info);*/
Message msg=new Message();
msg.what=READMSG;
Bundle bundle=new Bundle();
bundle.putString("msg",s);
msg.setData(bundle);
myhandler.sendMessage(msg);
}
}
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
客户端的接收以及服务器端的发送也是类似。