最近课上刚好需要做一个课程设计关于蓝牙的就挑选了个蓝牙聊天室,其实关键还是在于对蓝牙API的了解
一.蓝牙API
与蓝牙开发主要的相关类是以下四个
- BluetoothAdapter
字面上则理解为蓝牙适配器,打开蓝牙,关闭蓝牙,搜索设备,获取蓝牙Socket连接都是通过这个类来实现的。对应的方法就有- enable():用来打开蓝牙,一般在5.0后的话则会出现弹框提示是否开启蓝牙,其他则没有提示,也可以调用来进行弹窗打开
Intent intent =new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent );
disable():关闭蓝牙
getDefaultAdapter():获取蓝牙适配器BluetoothAdapter,只有这个方法来获取
getAddress():获取本地蓝牙的MAC地址,这个则是每一个蓝牙设备的唯一
getName():获取本地蓝牙的名称
getRemoteDevice(String address):获取蓝牙地址获取到远程的设备
startDiscovery():开启蓝牙设备搜索
cancelDiscovery():关闭扫描
listenUsingRfcommWithServiceRecord(serverSocketName,UUID):获取BluetoothServerSocket
-
BluetoothDevice
代表一个蓝牙设备createRfcommSocketToServiceRecord(UUIDuuid):根据UUID创建并返回一个BluetoothSocket
getName():获取蓝牙设备名称
getAddress():获取蓝牙的MAC地址
BluetoothServerSocket:类似于ServerSocket,方法都差不多,可以说蓝牙之间的通讯跟Socket相似。这个相当于服务器Socket
accept():这个方法会阻塞,直到连接建立,用来监听连接
close():关闭socket连接
-
BluetoothSocket:相当于客户端Socket
connect():用来向服务器BluetoothServerSocket发起连接
getInputStream():获取输入流
getOutputStream():获取输出流
close():关闭连接
getRemoteDevice():获取这个Socket连接的远程设备
二.搜索蓝牙设备
知道对应API后就可以进行对应的蓝牙开发,这里以获取蓝牙设备为例子
1.获取本地蓝牙适配器
BluetoothAdapter
mAdapter= BluetoothAdapter.getDefaultAdapter();
2.打开蓝牙
if(!mAdapter.isEnabled()){
//弹出对话框提示用户是后打开
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
//不做提示,强行打开
// mAdapter.enable();
}
3.搜索设备
mAdapter.startDiscovery(); //开启搜索
搜索设备的回调则需要通过注册广播的形式来获取
//发现设备的Action
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
//设备搜索完毕的action
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
定义广播
BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//当扫描到设备的时候
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 获取设备对象
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//提取强度信息
int rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);
Log.e(TAG, device.getName() + "\n" + device.getAddress() + "\n强度:" + rssi);
} //搜索完成
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
Log.e(TAG, "onReceive: 搜索完成" );
}
}
};
之后就可以进行个人的一些操作
三.蓝牙聊天的实现
要实现蓝牙聊天则涉及到蓝牙之间的传输通信,前面也说到了,这里肯定就是用到BluetoothServerSocket以及BluetoothSocket。
蓝牙传输通信相当于服务器端与客户端之间的通信,只不过不同是这里每一个蓝牙设备本身自己既充当服务器端也充当客户端,大致的关系就是
注意,这些连接都是阻塞式的,都要放在线程里去执行。
1.对于BluetoothServerSocket,建立一个AcceptThread,来监听是否有设备发起连接
//连接等待线程
class AcceptThread extends Thread{
private final BluetoothServerSocket serverSocket;
public AcceptThread(){
BluetoothServerSocket tmp = null;
try {
//获取实例
tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
} catch (IOException e) {
e.printStackTrace();
}
serverSocket = tmp;
}
@Override
public void run() {
super.run();
//监听是否有端口连接
BluetoothSocket socket = null;
while(mState != STATE_TRANSFER) {
try {
Log.e(TAG, "run: AcceptThread 阻塞调用,等待连接");
socket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "run: ActivityThread fail");
break;
}
//获取到连接Socket后则开始通信
if(socket != null){
synchronized (BluetoothChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
//传输数据,服务器端调用
Log.e(TAG, "run: 服务器AcceptThread传输" );
sendMessageToUi(MainActivity.BLUE_TOOTH_DIALOG , "正在与" + socket.getRemoteDevice().getName() + "连接");
//开始数据传输
dataTransfer(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_TRANSFER:
// 没有准备好或者终止连接
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket" + e);
}
break;
}
}
}
}
}
public void cancel(){
Log.e(TAG, "close: activity Thread" );
try {
if(serverSocket != null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "close: activity Thread fail");
}
}
}
可以看到,当BluetoothServerSocket监听到有设备连接的时候,就会调用dataTransfer开启一个数据传输。
2.同样如何发起连接BluetoothSocket呢
需要一个ConnectThread来发起
class ConnectThread extends Thread{
private final BluetoothSocket socket;
private final BluetoothDevice device;
public ConnectThread(BluetoothDevice device) {
this.device = device;
BluetoothSocket mSocket = null;
try {
//获取Socket
mSocket = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "ConnectThread: fail" );
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "连接失败,请重新连接");
}
socket = mSocket;
}
@Override
public void run() {
super.run();
//建立后取消扫描
bluetoothAdapter.cancelDiscovery();
try {
//开启连接
socket.connect();
} catch (IOException e) {
e.printStackTrace();
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
Log.e(TAG, "run: unable to close" );
}
//TODO 连接失败显示
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "连接失败,请重新连接");
BluetoothChatService.this.start();
}
// 重置
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
//连接建立,开始传输
dataTransfer(socket, device);
}
public void cancel(){
try {
if(socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
之后建立连接之后就会调用dataTransfer来进行数据传输,同样也需要一个线程来维护数据传输
class TransferThread extends Thread{
private final BluetoothSocket socket;
private final OutputStream out;
private final InputStream in;
public TransferThread(BluetoothSocket mBluetoothSocket){
socket = mBluetoothSocket;
OutputStream mOutputStream = null;
InputStream mInputStream = null;
try {
if(socket != null){
//获取连接的输入输出流
mOutputStream = socket.getOutputStream();
mInputStream = socket.getInputStream();
}
} catch (IOException e) {
e.printStackTrace();
}
out = mOutputStream;
in = mInputStream;
isTransferError = false;
}
@Override
public void run() {
super.run();
//读取数据
byte[] buffer = new byte[1024];
int bytes;
while (true){
try {
bytes = in.read(buffer);
//TODO 分发到主线程显示
uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_READ, bytes, -1, buffer)
.sendToTarget();
Log.e(TAG, "run: read " + new String(buffer , 0 , bytes) );
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "run: Transform error" + e.toString());
BluetoothChatService.this.start();
//TODO 连接丢失显示并重新开始连接
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "设备连接失败/传输关闭");
isTransferError = true;
break;
}
}
}
/**
* 写入数据传输
* @param buffer
*/
public void write(byte[] buffer) {
try {
out.write(buffer);
//TODO 到到UI显示
uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_WRAITE , -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write " + e);
}
}
public void cancel() {
try {
if(socket != null)
socket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed" + e);
}
}
}
蓝牙聊天则是基于上面三个线程来进行实现,同样,对于蓝牙文件间的传输也是同个道理,通过输入输出流来进行处理。之后的操作就比较容易处理了