Android 蓝牙传文件比较常见,但是官方也给出了基于蓝牙通讯做了个聊天室的sample,BluetoothChat。有兴趣的可以下载看下,很有意思。通讯那块用了特殊的BluetoothSocket。思路跟一般socket通讯一样。必须有服务端和客户端。sample有三个类:BluetoothChat,BluetoothChatService,DeviceListActivity。

BluetoothChat是主界面,可以看到聊天的内容,BluetoothChatService是功能类,实现了主要功能,但本身不是一个service,通过BluetoothChat的handle进行UI交互,DeviceListActivity是查找蓝牙设备的功能类

BluettohtChatService的实现逻辑是启动后就是服务端的在等待客户端来建立连接产生socket,当用户选了某个设备建立连接,那个设备就是客户端了,会把服务端的线程断掉。然后就利用BluetoothSocket通讯。用了状态模式来写,很有参考价值。不过很奇怪是一直有两个线程来等待设备接入,叫安全线程,不安全线程。功能一模一样都。

服务器端的实现

通过调用BluetoothAdapter的listenUsingRfcommWithServiceRecord(String, UUID)方法来获取BluetoothServerSocket(UUID用于客户端与服务器端之间的配对)

调用BluetoothServerSocket的accept()方法监听连接请求,如果收到请求,则返回一个BluetoothSocket实例(此方法为block方法,应置于新线程中)

如果不想在accept其他的连接,则调用BluetoothServerSocket的close()方法释放资源(调用该方法后,之前获得的BluetoothSocket实例并没有close。但由于RFCOMM一个时刻只允许在一条channel中有一个连接,则一般在accept一个连接后,便close掉BluetoothServerSocket

private classAcceptThread extendsThread{
private finalBluetoothServerSocketmmServerSocket;

publicAcceptThread(){
// UseatemporaryobjectthatislaterassignedtommServerSocket,
// becausemmServerSocketisfinal
BluetoothServerSockettmp= null;
try{
// MY_UUIDistheapp'sUUIDstring,alsousedbytheclientcode
tmp=mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID);
} catch(IOExceptione){}
mmServerSocket=tmp;
}

public voidrun(){
BluetoothSocketsocket= null;
// Keeplisteninguntilexceptionoccursorasocketisreturned
while( true){
try{
socket=mmServerSocket.accept();
} catch(IOExceptione){
break;
}
// Ifaconnectionwasaccepted
if(socket!= null){
// Doworktomanagetheconnection(inaseparatethread)
manageConnectedSocket(socket);
mmServerSocket.close();
break;
}
}
}

/** Willcancelthelisteningsocket,andcausethethreadtofinish */
public voidcancel(){
try{
mmServerSocket.close();
} catch(IOExceptione){}
}
}

客户端的实现

通过搜索得到服务器端的BluetoothService

调用BluetoothService的listenUsingRfcommWithServiceRecord(String, UUID)方法获取BluetoothSocket(该UUID应该同于服务器端的UUID)

调用BluetoothSocket的connect()方法(该方法为block方法),如果UUID同服务器端的UUID匹配,并且连接被服务器端accept,则connect()方法返回

注意:在调用connect()方法之前,应当确定当前没有搜索设备,否则连接会变得非常慢并且容易失败

private classConnectThread extendsThread{
private finalBluetoothSocketmmSocket;
private finalBluetoothDevicemmDevice;

publicConnectThread(BluetoothDevicedevice){
// UseatemporaryobjectthatislaterassignedtommSocket,
// becausemmSocketisfinal
BluetoothSockettmp= null;
mmDevice=device;

// GetaBluetoothSockettoconnectwiththegivenBluetoothDevice
try{
// MY_UUIDistheapp'sUUIDstring,alsousedbytheservercode
tmp=device.createRfcommSocketToServiceRecord(MY_UUID);
} catch(IOExceptione){}
mmSocket=tmp;
}

public voidrun(){
// Canceldiscoverybecauseitwillslowdowntheconnection
mBluetoothAdapter.cancelDiscovery();

try{
// Connectthedevicethroughthesocket.Thiswillblock
// untilitsucceedsorthrowsanexception
mmSocket.connect();
} catch(IOExceptionconnectException){
// Unabletoconnect;closethesocketandgetout
try{
mmSocket.close();
} catch(IOExceptioncloseException){}
return;
}

// Doworktomanagetheconnection(inaseparatethread)
manageConnectedSocket(mmSocket);
}

/** Willcancelanin-progressconnection,andclosethesocket */
public voidcancel(){
try{
mmSocket.close();
} catch(IOExceptione){}
}
}

连接管理(数据通信)

分别通过BluetoothSocket的getInputStream()和getOutputStream()方法获取InputStream和OutputStream

使用read(bytes[])和write(bytes[])方法分别进行读写操作

注意:read(bytes[])方法会一直block,知道从流中读取到信息,而write(bytes[])方法并不是经常的block(比如在另一设备没有及时read或者中间缓冲区已满的情况下,write方法会block)

private classConnectedThread extendsThread{
private finalBluetoothSocketmmSocket;
private finalInputStreammmInStream;
private finalOutputStreammmOutStream;

publicConnectedThread(BluetoothSocketsocket){
mmSocket=socket;
InputStreamtmpIn= null;
OutputStreamtmpOut= null;

// Gettheinputandoutputstreams,usingtempobjectsbecause
// memberstreamsarefinal
try{
tmpIn=socket.getInputStream();
tmpOut=socket.getOutputStream();
} catch(IOExceptione){}

mmInStream=tmpIn;
mmOutStream=tmpOut;
}

public voidrun(){
byte[]buffer= new byte[1024]; // bufferstoreforthestream
intbytes; // bytesreturnedfromread()

// KeeplisteningtotheInputStreamuntilanexceptionoccurs
while( true){
try{
// ReadfromtheInputStream
bytes=mmInStream.read(buffer);
// SendtheobtainedbytestotheUIActivity
mHandler.obtainMessage(MESSAGE_READ,bytes,-1,buffer)
.sendToTarget();
} catch(IOExceptione){
break;
}
}
}

/* CallthisfromthemainActivitytosenddatatotheremotedevice */
public voidwrite( byte[]bytes){
try{
mmOutStream.write(bytes);
} catch(IOExceptione){}
}

/* CallthisfromthemainActivitytoshutdowntheconnection */
public voidcancel(){
try{
mmSocket.close();
} catch(IOExceptione){}
}
}

更多相关文章

  1. Android之取消ViewPage+Fragment的预加载
  2. android浏览器研究-回退和前进
  3. 那些年,我们一起踩过的 “Android(安卓)坑”
  4. Drawable着色问题
  5. android sdk sdkmanger无界面使用方法(命令行更新SDK)
  6. Kotlin for Android使用教程(一)
  7. Service的onStartCommand方法的疑问
  8. 关于Spring for Android
  9. Android(安卓)学习之那些年我们遇到的BUG8:ArrayAdapter 直接使用

随机推荐

  1. 创建简历表格和注册表单
  2. 使用css浮动完成网页顶部导航
  3. 基本布局和块状元素
  4. 自定义导航条样式
  5. flex导航制作
  6. ROS2中使用Gtes示例
  7. 用户注册表练习
  8. 简历模板练习
  9. 理解弹性盒子
  10. 11.11flex布局导航