Autore Topic: Mantenere la connessione tra due activity con un service  (Letto 511 volte)

Offline Damien

  • Utente junior
  • **
  • Post: 63
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Lenovo ZUK Z1
  • Sistema operativo:
    Windows 10
Mantenere la connessione tra due activity con un service
« il: 31 Luglio 2016, 11:08:00 CEST »
0
Off-Topic:
Ho modificato questo post perchè ho capito la differente natura del problema
Buongiorno a tutti, sto sviluppando un'applicazione che si appoggia a un service per instaurare una connessione con un modulo bluetooth (RN4020). Funziona tutto benissimo, tuttavia ho riscontrato una piccola problematica. La mia app richiede dei dati ogni secondo e il modulo risponde. Quando passo da un'activity a un'altra utilizzando sempre lo stesso service si l'app si disconnette e si riconnette subito. Più che un problema di connessione credo sia semplicemente un problema nel codice del service. Quindi ho 2 activity e un service.

Il service è strutturato così:

Codice (Java): [Seleziona]
public class BluetoothLeService extends Service {

    private final static String TAG = BluetoothLeService.class.getSimpleName();         //Get name of service to tag debug and warning messages
    private BluetoothManager mBluetoothManager;                                         //BluetoothManager used to get the BluetoothAdapter
    private BluetoothAdapter mBluetoothAdapter;                                         //The BluetoothAdapter controls the BLE radio in the phone/tablet
    private BluetoothGatt mBluetoothGatt;                                               //BluetoothGatt controls the Bluetooth communication link
    private String mBluetoothDeviceAddress;                                             //Address of the connected BLE device

    public final static String ACTION_GATT_CONNECTED = "com.ferraricostruzioni.fcm_remoweed.ACTION_GATT_CONNECTED"; //Strings representing actions to broadcast to activities
    public final static String ACTION_GATT_DISCONNECTED = "com.ferraricostruzioni.fcm_remoweed.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.ferraricostruzioni.fcm_remoweed.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE = "com.ferraricostruzioni.fcm_remoweed.ACTION_DATA_AVAILABLE";
    public final static String ACTION_DATA_WRITTEN = "com.ferraricostruzioni.fcm_remoweed.ACTION_DATA_WRITTEN";
    public final static String EXTRA_DATA = "com.ferraricostruzioni.fcm_remoweed.EXTRA_DATA";

    public final static UUID UUID_MLDP_DATA_PRIVATE_CHARACTERISTIC = UUID.fromString(WorkActivity.MLDP_DATA_PRIVATE_CHAR);
    public final static UUID UUID_CHARACTERISTIC_NOTIFICATION_CONFIG = UUID.fromString(WorkActivity.CHARACTERISTIC_NOTIFICATION_CONFIG);

    private final IBinder mBinder = new LocalBinder();                                  //Binder for Activity that binds to this Service

    // ----------------------------------------------------------------------------------------------------------------
    // An activity has bound to this service
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;                                                                 //Return LocalBinder when an Activity binds to this Service
    }

    // ----------------------------------------------------------------------------------------------------------------
    // An activity has unbound from this service
    @Override
    public boolean onUnbind(Intent intent) {
        /*if (mBluetoothGatt != null) {                                                   //Check for existing BluetoothGatt connection
            mBluetoothGatt.close();                                                     //Close BluetoothGatt coonection for proper cleanup
            mBluetoothGatt = null;                                                      //No longer have a BluetoothGatt connection
        }*/

        return super.onUnbind(intent);
    }

    // ----------------------------------------------------------------------------------------------------------------
    // A Binder to return to an activity to let it bind to this service
    public class LocalBinder extends Binder {
        BluetoothLeService getService() {
            return BluetoothLeService.this;                                             //Return this instance of BluetoothLeService so clients can call its public methods
        }
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Implements callback methods for GATT events that the app cares about.  For example: connection change and services discovered.
    // When onConnectionStateChange() is called with newState = STATE_CONNECTED then it calls mBluetoothGatt.discoverServices()
    // resulting in another callback to onServicesDiscovered()
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { //Change in connection state
            if (newState == BluetoothProfile.STATE_CONNECTED) {                         //See if we are connected
                broadcastUpdate(ACTION_GATT_CONNECTED);                                 //Go broadcast an intent to say we are connected
                Log.i(TAG, "Connected to GATT server, starting service discovery");
                mBluetoothGatt.discoverServices();                                      //Discover services on connected BLE device
            }
            else if (newState == BluetoothProfile.STATE_DISCONNECTED) {                 //See if we are not connected
                broadcastUpdate(ACTION_GATT_DISCONNECTED);                              //Go broadcast an intent to say we are disconnected
                Log.i(TAG, "Disconnected from GATT server.");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {              //BLE service discovery complete
            if (status == BluetoothGatt.GATT_SUCCESS) {                                 //See if the service discovery was successful
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);                       //Go broadcast an intent to say we have discovered services
            }
            else {                                                                      //Service discovery failed so log a warning
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        //For information only. This application uses Indication to receive updated characteristic data, not Read
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //A request to Read has completed
            if (status == BluetoothGatt.GATT_SUCCESS) {                                 //See if the read was successful
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);                 //Go broadcast an intent with the characteristic data
            }
        }

        //For information only. This application sends small packets infrequently and does not need to know what the previous write completed
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //A request to Write has completed
            if (status == BluetoothGatt.GATT_SUCCESS) {                                 //See if the write was successful
                broadcastUpdate(ACTION_DATA_WRITTEN, characteristic);                   //Go broadcast an intent to say we have have written data
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { //Indication or notification was received
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);                     //Go broadcast an intent with the characteristic data
        }
    };

    // ----------------------------------------------------------------------------------------------------------------
    // Broadcast an intent with a string representing an action
    private void broadcastUpdate(final String action) {
        final Intent intent = new Intent(action);                                       //Create new intent to broadcast the action
        sendBroadcast(intent);                                                          //Broadcast the intent
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Broadcast an intent with a string representing an action an extra string with the data
    // Modify this code for data that is not in a string format
    private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
        final Intent intent = new Intent(action);                                       //Create new intent to broadcast the action
        if(action.equals(ACTION_DATA_AVAILABLE)) {                                      //See if we need to send data
            if (UUID_MLDP_DATA_PRIVATE_CHARACTERISTIC.equals(characteristic.getUuid())) { //See if this is the correct characteristic
                String dataValue = characteristic.getStringValue(0);                    //Get the data (in this case it is a string)
                intent.putExtra(EXTRA_DATA, dataValue);                                 //Add the data string to the intent
            }
        }
        else {                                                                          //Did not get an action string we expect
            Log.d(TAG, "Action: " + action);
        }
        sendBroadcast(intent);                                                          //Broadcast the intent
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Initialize by getting the BluetoothManager and BluetoothAdapter
    public boolean initialize() {
        if (mBluetoothManager == null) {                                                //See if we do not already have the BluetoothManager
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); //Get the BluetoothManager
            if (mBluetoothManager == null) {                                            //See if we failed
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;                                                           //Report the error
            }
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();                             //Ask the BluetoothManager to get the BluetoothAdapter
        if (mBluetoothAdapter == null) {                                                //See if we failed
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;                                                               //Report the error
        }

        return true;                                                                    //Success, we have a BluetoothAdapter to control the radio
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Open a BluetoothGatt connection to a BLE device given its address
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {                             //Check that we have a Bluetooth adappter and device address
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");     //Log a warning that something went wrong
            return false;                                                               //Failed to connect
        }

        // Previously connected device.  Try to reconnect.
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { //See if there was previous connection to the device
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            if (mBluetoothGatt.connect()) {                                             //See if we can connect with the existing BluetoothGatt to connect
                return true;                                                            //Success
            }
            else {
                return false;                                                           //Were not able to connect
            }
        }

        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);      //No previous device so get the Bluetooth device by referencing its address
        if (device == null) {                                                           //Check whether a device was returned
            Log.w(TAG, "Device not found.  Unable to connect.");                        //Warn that something went wrong
            return false;                                                               //Failed to find the device
        }

        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);                //Directly connect to the device so autoConnect is false
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;                                              //Record the address in case we bneed to reconnect with the existing BluetoothGatt
        return true;
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Retrieve and return a list of supported GATT services on the connected device
    public List<BluetoothGattService> getSupportedGattServices() {
        if (mBluetoothGatt == null) {                                                   //Check that we have a valid GATT connection
            return null;
        }
        return mBluetoothGatt.getServices();                                            //Get the list of services
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Disconnects an existing connection or cancel a pending connection
    // BluetoothGattCallback.onConnectionStateChange() will get the result
    public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have a GATT connection to disconnect
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();                                                    //Disconnect GATT connection
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Request a read of a given BluetoothGattCharacteristic. The Read result is reported asynchronously through the
    // BluetoothGattCallback onCharacteristicRead callback method.
    // For information only. This application uses Indication to receive updated characteristic data, not Read
    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have access to a Bluetooth radio
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);                              //Request the BluetoothGatt to Read the characteristic
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Write to a given characteristic. The completion of the write is reported asynchronously through the
    // BluetoothGattCallback onCharacteristicWrire callback method.
    public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have access to a Bluetooth radio
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        int test = characteristic.getProperties();                                      //Get the properties of the characteristic
        if ((test & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 && (test & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { //Check that the property is writable
            return;
        }

        if (mBluetoothGatt.writeCharacteristic(characteristic)) {                       //Request the BluetoothGatt to do the Write
            Log.d(TAG, "writeCharacteristic successful");                               //The request was accepted, this does not mean the write completed
        }
        else {
            Log.d(TAG, "writeCharacteristic failed");                                   //Write request was not accepted by the BluetoothGatt
        }
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Enable notification on a characteristic
    // For information only. This application uses Indication, not Notification
    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have a GATT connection
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);          //Enable notification and indication for the characteristic

//        if (UUID_MLDP_DATA_PRIVATE_CHARACTERISTIC.equals(characteristic.getUuid())) {
        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_CHARACTERISTIC_NOTIFICATION_CONFIG); //Get the descripter that enables notification on the server
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);         //Set the value of the descriptor to enable notification
        mBluetoothGatt.writeDescriptor(descriptor);                                     //Write the descriptor
//        }
    }

    // ----------------------------------------------------------------------------------------------------------------
    // Enable indication on a characteristic
    public void setCharacteristicIndication(BluetoothGattCharacteristic characteristic, boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {                      //Check that we have a GATT connection
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);          //Enable notification and indication for the characteristic

        // This is specific to our custom profile
//        if (UUID_MLDP_DATA_PRIVATE_CHARACTERISTIC.equals(characteristic.getUuid())) {
        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_CHARACTERISTIC_NOTIFICATION_CONFIG); //Get the descripter that enables indication on the server
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);           //Set the value of the descriptor to enable indication
        mBluetoothGatt.writeDescriptor(descriptor);                                     //Write the descriptor
//        }
    }
   
}

Mentre nella prima activity nell'onCreate ho queste tre righe qui:

Codice (Java): [Seleziona]
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);          
startService(gattServiceIntent);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

Nell'onPause chiamo un unBind();

e nella seconda activity chiamo solamente un bindService();

Dove sbaglio? Perchè mi si disconnette e crasha sulla seconda activity non appena mando qualcosa al modulo bluetooth?
« Ultima modifica: 03 Agosto 2016, 14:27:37 CEST da Damien »
Se ti sono stato d'aiuto premi il tasto THANKS :)

Offline Damien

  • Utente junior
  • **
  • Post: 63
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Lenovo ZUK Z1
  • Sistema operativo:
    Windows 10
Re:Mantenere la connessione tra due activity con un service
« Risposta #1 il: 24 Agosto 2016, 12:06:17 CEST »
0
UP! Nessuno sa aiutarmi? Secondo me dovrebbe essere solo un problemino di gestione del service, saranno solo un paio di righe sbagliate ma che non riesco a capire dove
Se ti sono stato d'aiuto premi il tasto THANKS :)

Offline DCode

  • Utente junior
  • **
  • Post: 69
  • Respect: +20
    • Mostra profilo
  • Sistema operativo:
    Ubuntu 16.04 LTS
Re:Mantenere la connessione tra due activity con un service
« Risposta #2 il: 25 Agosto 2016, 14:49:28 CEST »
+1
Io personalmente creerei una classe che estende Application dove nell'onCreate istanzi il tuo service, in modo poi che in tutte le activity ci accedi tramite context [ getApplicationContext() ].

Non dimenticarti di mettere poi la classe come delegato della tua app nel manifest.

Buona giornata!
┻━┻ ︵ヽ(°□°ヽ) Develop w/ ( ( ObjC || Swift ) && Java[ "Android" ] ) (╯°□°)╯︵ ┻━┻

Offline tonno16

  • Utente storico
  • *****
  • Post: 1169
  • Respect: +56
    • Mostra profilo
  • Dispositivo Android:
    moto g
  • Play Store ID:
    Diego Tonini
  • Sistema operativo:
    OpenSuse
Re:Mantenere la connessione tra due activity con un service
« Risposta #3 il: 25 Agosto 2016, 19:20:24 CEST »
0
Un service bound è come un server. La tua activity fa il bond essendo in client. Esso esiste finché tutti i client sono attivi, altrimenti no. Prova a fare un service normale e comunica con gli intento. Oppure USA librerie come eventbus. O USA dei broadcast receiver

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Mantenere la connessione tra due activity con un service
« Risposta #4 il: 26 Agosto 2016, 09:58:12 CEST »
0
Codice (Java): [Seleziona]
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);          
startService(gattServiceIntent);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

Domanda: perché fai sia la startService che la bindService? Non dovrebbe essere sufficiente la bindService? Prova a commentarmi la startService.

Citazione
Nell'onPause chiamo un unBind();

Normalmente si tende a chiamarlo nella onDestroy ma nel tuo caso passando da una activity all'altra forse è meglio lasciarlo così ...

Citazione
e nella seconda activity chiamo solamente un bindService();
Dove sbaglio? Perchè mi si disconnette e crasha sulla seconda activity non appena mando qualcosa al modulo bluetooth?

A mio avviso rimane attivo il service fatto partire dalla startService visto che non è legato al ciclo di vita della prima activity e quindi alla chiusura ammazzi il secondo service che "dovrebbe" essere in coda al primo se non erro ...

PS. Posso chiederti con cosa comunichi; il modulo bluetooth a cosa è collegato?
« Ultima modifica: 26 Agosto 2016, 10:00:22 CEST da wlf »

Offline tonno16

  • Utente storico
  • *****
  • Post: 1169
  • Respect: +56
    • Mostra profilo
  • Dispositivo Android:
    moto g
  • Play Store ID:
    Diego Tonini
  • Sistema operativo:
    OpenSuse
Re:Mantenere la connessione tra due activity con un service
« Risposta #5 il: 26 Agosto 2016, 11:04:55 CEST »
0
È giusto metterlo dove lai messo nella maggior parte di volte secondo me. E poi non esiste la code di service. Non vengono creato 2 service come pensi. Bensì viene chiama nuovamente onStartCommand()

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Mantenere la connessione tra due activity con un service
« Risposta #6 il: 26 Agosto 2016, 12:34:32 CEST »
+1
È giusto metterlo dove lai messo nella maggior parte di volte secondo me.

Io per il BluetoothLeService lo utilizzo nella onDestroy perché i tempi col bluetooth non sono poi così serrati ...
Lo stesso esempio del BluetoothLeService da cui ha preso lo utilizza nell'onDestroy.

Citazione
E poi non esiste la code di service. Non vengono creato 2 service come pensi. Bensì viene chiama nuovamente onStartCommand()

Sempre nell'esempio citato nella onCreate per far partire il service viene bindato e fatto partire con:

Codice: [Seleziona]
        Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
        bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

Mettendoci la startService in mezzo io mi attendevo che lanciasse un service slegato all'activity chiamante e subito dietro un altro service legato al ciclo di vita dell'activity.
Se non ricordo male facendo partire due volte lo stesso service (non bind) il secondo non parte finché il primo non è terminato; facendo la stessa cosa con un asyncTask invece mi ritrovavo con 2 processi contemporanei.

Offline Damien

  • Utente junior
  • **
  • Post: 63
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Lenovo ZUK Z1
  • Sistema operativo:
    Windows 10
Re:Mantenere la connessione tra due activity con un service
« Risposta #7 il: 17 Settembre 2016, 15:07:46 CEST »
0
Domanda: perché fai sia la startService che la bindService? Non dovrebbe essere sufficiente la bindService? Prova a commentarmi la startService.

Normalmente si tende a chiamarlo nella onDestroy ma nel tuo caso passando da una activity all'altra forse è meglio lasciarlo così ...

A mio avviso rimane attivo il service fatto partire dalla startService visto che non è legato al ciclo di vita della prima activity e quindi alla chiusura ammazzi il secondo service che "dovrebbe" essere in coda al primo se non erro ...

PS. Posso chiederti con cosa comunichi; il modulo bluetooth a cosa è collegato?

Allora il cellulare comunica con un modulo bluetooth collegato a un microcontrollore, un PIC32 (anch'esso programmato da me, ma lui fa quello che deve fare).
Ho scoperto che oltre ai 2 classici tipi di Service ovvero started e bound vi è un terzo tipo ovvero started and bound. Io utilizzo quest'ultimo che crea prima il service (indipendente dalla classe) con:
Codice (Java): [Seleziona]
startService(gattServiceIntent);e poi lo collega con:
Codice (Java): [Seleziona]
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);In teoria in questo modo è creato il service ma è indipendente dal ciclo di vita della mia classe. Quello che non capisco bene è il fatto che quando utilizzo unBind() oltre a slegarsi dalla mia activity, per poi collegarsi alla seconda activity, si distrugga o meno

Io personalmente creerei una classe che estende Application dove nell'onCreate istanzi il tuo service, in modo poi che in tutte le activity ci accedi tramite context [ getApplicationContext() ].

Non dimenticarti di mettere poi la classe come delegato della tua app nel manifest.

Buona giornata!

Questo modo credo possa funzionare, ma non capisco bene come implementarlo (anche perchè la primissima activity della mia app mostra una lista di dispositivi disponibili a cui collegarsi)


L'unbind l'avevo messo nell'onPause perchè quando apro la mia seconda activity il service si slega dalla prima activity per collegarsi alla seconda



EDIT 18/09:
Ho fatto altre prove e ho scoperto che se nel Service nell'onUnBind aggiungo qualche riga per la disconnessione, ovvero:
Codice (Java): [Seleziona]
if (mBluetoothGatt != null) {                                                   //Check for existing BluetoothGatt connection
            mBluetoothGatt.close();                                                     //Close BluetoothGatt coonection for proper cleanup
            mBluetoothGatt = null;                                                      //No longer have a BluetoothGatt connection
        }
mi va nell'activity seguente e fa quello che deve fare perfettamente se non per il fatto che si disconnette per qualche secondo disturbando il mio microcontrollore. Se invece tolgo quelle righe li appena cerco di aprire mi crasha l'activity. Ho verificato che il service non si distruggesse nell'unbind e effettivamente non si distrugge
« Ultima modifica: 18 Settembre 2016, 15:48:52 CEST da Damien »
Se ti sono stato d'aiuto premi il tasto THANKS :)