目录
一、需要达到的效果
能用蓝牙在设备见进行简单通信。
二、项目结构
三、代码部分
1.申请权限
在AndroidManifest.xml中添加如下权限
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
2.DeviceList —— 蓝牙搜索设备功能
蓝牙搜索主要是要实现一个BroadcastReceiver 广播接收者。
首先要注册广播,才能使用,添加蓝牙设备ACTION_FOUND等,如下
private IntentFilter filter = new IntentFilter();
…………………………………………………
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver,filter);
其次就是这里广播接收
一旦开启广播搜索,并找到设备 接收到ACTION_FOUND的信息,则可以从BluetoothDevice.EXTRA_DEVICE中得到搜索到的设备信息,显示到屏幕上。
如果是DISCOVERY_FINISHED,即结束搜索。
private final BroadcastReceiver mReceiver = 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); // 得到设备信息
}else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED
.equals(action)){ //结束搜索了 }
}
}
};
3.ChatService —— 蓝牙连接功能
通过下列3个线程来实现。
1) AcceptThread —— 被动等待连接的线程
使用BluetoothServerSocket accept函数,收到连接,则保持此连接传给 ConnectedThread
// 创建监听线程,准备接受新连接。使用阻塞方式,调用BluetoothServerSocket.accept()
private class AcceptThread extends Thread{
private final BluetoothServerSocket mmServerSocket;
public AcceptThread(){
BluetoothServerSocket tmp = null;
try{
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID);
}catch (IOException e){}
mmServerSocket = tmp;
}
public void run(){
BluetoothSocket socket= null;
while(mState != STATE_CONNECTED){
try{
socket = mmServerSocket.accept();
}catch (IOException e) {
break;
}
if(socket != null){
connected(socket,socket.getRemoteDevice());
try{
mmServerSocket.close();
}catch (IOException e){}
}
}
}
public void cancel(){
try{
mmServerSocket.close();
}catch (IOException e){}
}
}
2) ConnectThread —— 主动发起连接的线程
createRfcommSocketToServiceRecord 根据UUID 发起连接,连接成功则将连接传入ConnectedThread
// 连接线程,专门用来对外发出连接对方蓝牙的请求并进行处理
// 构造函数里通过BluetoothDevice.createRfcommSocketToServiceRecord(),
// 从待连接的device产生BluetoothSocket,然后在run方法中connect
// 成功后调用 BluetoothChatService的connnected()方法,定义cancel()在关闭线程时能关闭socket
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 final
mmDevice=device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try{
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
}catch (IOException e){}
mmSocket = tmp;
}
public void run(){
// Cancel discovery because it will slow down the connection
mAdapter.cancelDiscovery();
try{
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
}catch (IOException e){
connectionFailed();
// Unable to connect; close the socket and get out
try{
mmSocket.close();
}catch (IOException e2){}
//ChatService.this.start();
return;
}
synchronized(ChatService.this){
mConnectedThread = null;
}
connected(mmSocket,mmDevice);
}
public void cancel(){
/* try{
mmSocket.close();
}catch (IOException e){}*/
}
}
3) ConnectedThread —— 管理连接的线程
管理连接好的双方,进行数据通信。输入输出流,接收数据,发送数据
// 双方蓝牙连接后一直运行的线程。构造函数中设置输入输出流。
// Run方法中使用阻塞模式的InputStream.read()循环读取输入流
// 然后psot到UI线程中更新聊天信息。也提供了write()将聊天消息写入输出流传输至对方,
// 传输成功后回写入UI线程。最后cancel()关闭连接的socket
private class ConnectedThread extends Thread{
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket){
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut=null;
// Get the input and output streams, using temp objects because
// member streams are final
try{
tmpIn=mmSocket.getInputStream();
tmpOut=mmSocket.getOutputStream();
}catch (IOException e){}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run(){
byte[]buffer=new byte[1024];
int bytes;
while (true){
try{
bytes = mmInStream.read(buffer);
mHandler.obtainMessage(MainActivity.MESSAGE_READ,bytes,-1,buffer).sendToTarget();
}catch (IOException e){
connectionLost();
break;
}
}
}
public void write(byte[]buffer){
try{
mmOutStream.write(buffer);
}catch (IOException e){
Log.d("MainActivity","Send Fail");
}
mHandler.obtainMessage(MainActivity.MESSAGE_WRITE,buffer).sendToTarget();
}
public void cancel(){
try{
mmSocket.close();
}catch (IOException e){}
}
}
4. MainActivity—— 主聊天界面
将双方信息显示在上方,具体代码见文章末尾代码仓库地址
四、效果图:
实现的功能有开启和关闭蓝牙、查找设备、打开设备可见、以及设备间的通信
主聊天界面:Listview显示对话,EditText编辑消息,点击Button发送消息
设备列表:两个ListView分别显示已配对和搜索到的设备,点击Button按钮开始查找设备。
需要注意设备在配对前需要开启设备可见