我有一个Android应用程序,旨在与其他手机配对,并通过蓝牙发送/接收数据。具体来说,当用户点击按钮时,它将开始发现,连接到手机,打开套接字等。一般情况下,这也有效,我也想设置它,以便在整个过程中,如果用户点击再次按下按钮,它将正确关闭/停止此过程。我遇到的问题是,当用户点击停止时,有时它会在应用程序仍在尝试检查传入数据时过早关闭套接字,因为这一切都发生在多个线程中。我正试图找到正确的方法来处理这个
因此,当用户单击以启动该过程时,将发生发现,然后它将尝试通过套接字连接到配对设备:
private class ConnectThread extends Thread {
private 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;
}
public void run() {
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
while(!this.isInterrupted()){
BluetoothSocket tmp = null;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// SERVICE_UUID is the app's UUID string, also used by the server code
tmp = mmDevice.createRfcommSocketToServiceRecord(SERVICE_UUID);
} catch (IOException e) {
e.printStackTrace();
}
mmSocket = tmp;
if(mmSocket != null){
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
Log.d(TAG, "Socket connected");
// Do work to manage the connection (in a separate thread)
connectedThread = new ConnectedThread(mmSocket);
connectedThread.start();
break;
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
Log.d(TAG, "Unable to connect socket: " + connectException.getMessage());
}
}
}
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
Log.d(TAG, "Closing socket");
mmSocket.close();
this.interrupt();
} catch (IOException e) {
e.printStackTrace();
}
}
}
套接字连接后,我启动另一个线程来设置数据流:
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 = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (!this.isInterrupted()) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
this.interrupt();
} catch (IOException e) {
e.printStackTrace();
}
}
}
所以这一切都可以发送/接收数据。但是当用户第二次单击该按钮以停止整个过程时(如果连接已经存在则终止连接)我收到错误。按下Stop后,将执行以下代码:
if(btHelper.discoveryStarted){
this.discoveryStarted = false;
mBluetoothAdapter.cancelDiscovery();
}
//Stop socket connection
if(connectThread != null & connectedThread != null){
connectedThread.cancel();
connectThread.cancel();
}
我得到的错误是ioexception:bt socket closed, read return: -1
,它指向我试图从数据流中读取的行:
bytes = mmInStream.read(buffer)
所以问题是它试图从流中读取,但是在按钮单击时我已经告诉套接字关闭,因此没有套接字可以读取。什么是中断这些线程的正确方法,以避免像这样的竞争条件?
此外,如何进行此设置,以便无论用户在此过程中的哪个位置,相应的线程/套接字/连接都将正确关闭? (例如,有时套接字将被打开,但数据流尚未设置,与两者/都没有创建)