kai-morich / SimpleBluetoothLeTerminal

Android terminal app for Bluetooth LE devices using custom serial profiles
MIT License
288 stars 111 forks source link

Bind to service from a different activity issue #12

Closed yudhiApp closed 4 years ago

yudhiApp commented 4 years ago

Hi, I am trying to bind to the serial service from a different activity after I connect to a bluetooth LE device. I can see that the service is running in the background "D/SerialSocket: read, len=61" being printed. In my activity the I can see in the log that it is connected. But I cannot receive any data onSerialRead. Is that the right way to connect?

` public class BoundToService extends AppCompatActivity implements ServiceConnection, SerialListener {

private SerialService serialService;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_bound_to_service);
    bindService(new Intent(getApplicationContext(), SerialService.class),this, Context.BIND_AUTO_CREATE);
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    Log.i("OnServiceCONNECTED ", "On connected");
    serialService = ((SerialService.SerialBinder) service).getService();
}

@Override
public void onServiceDisconnected(ComponentName name) { }
@Override
public void onSerialConnect() {Log.i("SERIAL", "onSerialConnect(");}
@Override
public void onSerialConnectError(Exception e) {Log.i("SERIAL", "onSerialConnectError");}
@Override
public void onSerialRead(byte[] data) {Log.i("onSerialRead ", new String(data)); }
@Override
public void onSerialIoError(Exception e) {Log.i("SERIAL", "onSerialIoError"); }

}`

kai-morich commented 4 years ago

additionally you have to call serialService.connect() to set your activity as listener. You might run into issues as the socket and service are distinct objects and the service has no reference to the socket and therefore cannot keep the socket open when the first activity stopped. In my play store apps I redesigned this in the meantime to have the socket as service member.

yudhiApp commented 4 years ago

Hi Kai, Thank you for your reply. I did some changes as shown below, it works. But with the issues that you mentioned. Maybe I should stick with only one activity. I tried something: in the onDestroy method in TerminalFragment, I did this to let the service run:

@Override public void onDestroy() { getActivity().unbindService(this); super.onDestroy(); }

And in my second activity I did this:

`......... import de.kai_morich.simple_bluetooth_le_terminal.SerialService.SerialBinder;

public class BoundToService extends AppCompatActivity implements SerialListener{

private SerialService serialService;
boolean isBound = false;
private SerialSocket socket;
TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_bound_to_service);
    textView = findViewById(R.id.textViewBound);
    textView.setMovementMethod(ScrollingMovementMethod.getInstance());
    Intent intent = new Intent(this, SerialService.class);
    bindService(intent,bleConnection,Context.BIND_AUTO_CREATE);
}

private ServiceConnection bleConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        SerialBinder binder = (SerialBinder) service;
        serialService = binder.getService();
        isBound = true;
        connect(); //connect here
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
        unbindService(bleConnection);
        isBound = false;
    }
};

private void receive(byte[] data) {
    textView.append(new String(data));
}

private void connect() {
    try {
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(TerminalFragment.deviceAddress); //static
        String deviceName = device.getName() != null ? device.getName() : device.getAddress();
        socket = new SerialSocket();
        serialService.connect(this, "Connected to " + deviceName);
        socket.connect(this, serialService, device);
    } catch (Exception e) {
        onSerialConnectError(e);
    }
}
@Override
public void onSerialConnect() {    }
@Override
public void onSerialConnectError(Exception e) {e.printStackTrace();}
@Override
public void onSerialRead(byte[] data) {
    receive(data);
}
@Override
public void onSerialIoError(Exception e) {  }

}`

kai-morich commented 4 years ago

please try latest version, where the socket is a member of the service

yudhiApp commented 4 years ago

Oh thank you, so the socket is now a member of the service. Does it mean, that once the service is started, I can disconnect to the service and reconnect again and the service will always have a reference to the socket? Did you a method to stop the service (selfstop()) to allow the BLE device to disconnect from the service instead of having to unplug the ble or send commands through the terminal? Cheers

kai-morich commented 4 years ago

the service now holds the connection open, while the activity is gone. Another activity can attach to the service to recieve & send data. With disconnect you can close the socket.