单击按钮时的Android中断线程 - 避免竞争条件

时间:2016-06-06 07:30:53

标签: java android multithreading sockets android-bluetooth

我有一个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)

所以问题是它试图从流中读取,但是在按钮单击时我已经告诉套接字关闭,因此没有套接字可以读取。什么是中断这些线程的正确方法,以避免像这样的竞争条件?

此外,如何进行此设置,以便无论用户在此过程中的哪个位置,相应的线程/套接字/连接都将正确关闭? (例如,有时套接字将被打开,但数据流尚未设置,与两者/都没有创建)

0 个答案:

没有答案