0

android app implementation: MyActivity.java is the main interface, where mHandler receives messages from the peer Bluetooth, BtClientService.java defines the Bluetooth initialization, connection, read and write functions, May I ask how to implement the function of vibrating to remind if no bluetooth data is received from the peer within 10s?

  1. Message receiving and processing in the MyActivity.mHandler, but this is not appropriate here, here is the processing of received messages;
  2. InputStream.available() is processed in BtClientService to determine whether the peer data has been received. But the question here is, how to continuously monitor the data reception of the peer within 10s?

Another one, if the peer data is not received within 10s (in BtClientService.ConnectThread), how to let MyActivity know? handler. sendMessage?

in MyActivity.mHandler:

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case Constants.MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                String readMessage = new String(readBuf, 0, msg.arg1);
                mConversationArrayAdapter.add(mConnectedDeviceName + ":  " + readMessage);

                isReceived = !TextUtils.isEmpty(readMessage.trim());
                Log.d(TAG, "mHandler - MESSAGE_READ - isReceived = " + isReceived);
                break;
            case Constants.MESSAGE_DEVICE_NAME:
                // save the connected device's name
                mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to "
                        + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                break;

in BtClientService.ConnectThread

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket, String socketType) {
        Log.d(TAG, "create ConnectedThread: " + socketType);
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the BluetoothSocket input and output streams
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            Log.e(TAG, "temp sockets not created ~ " + e.toString());
        }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
        mState = STATE_CONNECTED;
    }

    public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;

        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            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) {
                Log.e(TAG, "disconnected ~ " + e.toString());
                connectionLost();
                break;
            }
        }
    }
PETRER
  • 1
  • 1
  • 1
    It can be done in several ways but its implementation depends on your code. So consider adding a [minimal reproducible code](https://stackoverflow.com/help/minimal-reproducible-example) so that we can suggest you the possible better solution based on your code. – Kozmotronik Jul 03 '23 at 11:13
  • Do you have any ideas? – PETRER Jul 05 '23 at 06:17

1 Answers1

0

Here are several solutions to implement a logic to detect 10 secs time out in your BtClientService.ConnectThread for each connection apart. You can choose any of it as per your convenience.

First let's get started by defining a new message code called MESSAGE_TIMEOUT in your related class so that we can let the main thread know that a 10 secs time out has occured.

class Constants {
    ...
    public static final int MESSAGE_TIMEOUT = 5 ///< Give it an appropriate value
    ...
}

Add a new case statement to your handler in order to handle the time out.

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case Constants.MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                String readMessage = new String(readBuf, 0, msg.arg1);
                mConversationArrayAdapter.add(mConnectedDeviceName + ":  " + readMessage);
                
                isReceived = !TextUtils.isEmpty(readMessage.trim());
                Log.d(TAG, "mHandler - MESSAGE_READ - isReceived = " + isReceived);
                break;
            case Constants.MESSAGE_DEVICE_NAME:
                // save the connected device's name
                mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to "
                + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                break;
            case Constants.MESSAGE_TIMEOUT:
                // Handle 10sec timeout here
                break;
        }
    }
}

Here it comes to implementing the 10 secs time out logic in several different APIs.

Solution 1

Using the sendMessageDelayed and removeMessages pair to post a time out message after 10 secs if no data has received. If any data available within 10 seconds the posted message will be cancelled so it will not get executed.

private class ConnectedThread extends Thread {
    ...
    
    public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;
        
        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            try {
                // Schedule to send the time out message after 10 secs
                Message msg = mHandler.obtainMessage(Constants.MESSAGE_TIMEOUT);
                mHandler.sendMessageDelayed(msg, 10000);
                
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                
                // There is new data from remote, cancel the 10secs timeout msg
                mHandler.removeMessages(Constants.MESSAGE_TIMEOUT);
                
                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
                .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "disconnected ~ " + e.toString());
                connectionLost();
                break;
            }
        }
    }
}

Solution 2

Using the postDelayed and removeCallbacks pair.

private class ConnectedThread extends Thread {
    ...
    
    public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;
        
        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            try {
                // Schedule to send the time out message after 10 secs
                Runnable runnable = () -> {
                    mHandler.obtainMessage(Constants.MESSAGE_TIMEOUT).sendToTarget();
                }
                mHandler.postDelayed(runnable, 10000);
                
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                
                // There is new data from remote, cancel the 10secs timeout msg
                mHandler.removeCallbacks(runnable);
                
                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
                .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "disconnected ~ " + e.toString());
                connectionLost();
                break;
            }
        }
    }
}

Solution 3

Using the ScheduledExecutorService and ScheduledFeature.

private class ConnectedThread extends Thread {
    ...
    private final ScheduledExecutorService schexecutor = Executors.newSingleThreadScheduledExecutor();
    
    public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;
        
        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            try {
                // Schedule to send the time out message after 10 secs
                Runnable runnable = () -> {
                    mHandler.obtainMessage(Constants.MESSAGE_TIMEOUT).sendToTarget();
                }
                ScheduledFeature<?> sf = schexecutor.schedule(runnable, 10, TimeUnit.SECONDS);
                
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                
                // There is new data from remote, cancel the 10secs timeout msg
                sf.cancel(/* mayInterruptIfRunning */ true); 
                
                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
                .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "disconnected ~ " + e.toString());
                connectionLost();
                break;
            }
        }
        // Shut down the scheduled executor service immediately upon exiting this thread
        schexecutor.shutdownNow();
    }
}

Solution 4

Using the Timer and TimerTask.

private class ConnectedThread extends Thread {
    ...
    private final Timer timer = new Timer(/* isDaemon */ true);
    
    public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;
        
        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            try {
                // Schedule to send the time out message after 10 secs
                TimerTask timerTask = () -> {
                    mHandler.obtainMessage(Constants.MESSAGE_TIMEOUT).sendToTarget();
                }
                timer.schedule(timerTask, 10000);
                
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                
                // There is new data from remote, cancel the 10secs timeout msg
                timerTask.cancel(); 
                
                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
                .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "disconnected ~ " + e.toString());
                connectionLost();
                break;
            }
        }
    }
}

Of course there are more approaches that can be applied for this case. But here, I've provided the best approches for your case as far as I know. Just pick one of them as per your convenience since any of them will do the same job.

Finally regarding how to control vibration in Android, see this good old and rich of information post to implement it in your application.

Kozmotronik
  • 2,080
  • 3
  • 10
  • 25