2013-03-17 5 views
7

Domanda:
Come posso gestire (collegare, leggere, scrivere, disconnettere) una connessione Bluetooth che permane attraverso le modifiche di configurazione?Come gestire una connessione Bluetooth su Android attraverso le modifiche alla configurazione?

Preferisco soluzioni che siano compatibili con la versione dispositivo 2.2 "Froyo" utilizzando ActionBarSherlock.

problemi ...

  • BluetoothDeviceBluetoothSocket possono essere conservati in onSaveState.

  • Per il keep my app responsive, il blocco di 12 secondi BluetoothSocket.connect() deve essere eseguito su una filettatura separata. L'avvio di un Runnable è il metodo consigliato per eseguire il thread di attività lunghe, ma è un incubo che tenta di ripristinare una modifica di configurazione. I documenti ufficiali indicano tre diverse soluzioni.

    • Utilizzare getLastNonConfigurationInstance(), che è deprecato (sul serio ?!).

    • Impostare android:configChanges="keyboardHidden|orientation" come BluetoothChat Sample. Tuttavia, questo non tiene conto di tutti i tipi di modifiche alla configurazione.

    • Annulla & attività di riavvio come Shelves Example. In questo caso, questo potrebbe potenzialmente sprecare altri 12 secondi.

Aggiornamento 1

  • ulteriori ricerche mi hanno portato a asyncTaskLoader, ma sembra che questo può solo aggiornare l'interfaccia utente al termine, e non può fornire gli aggiornamenti.

  • campione Il BluetoothHDP utilizza un servizio. I servizi sembrano incentrati sulla comunicazione tra processi e sulla necessità di andare oltre il ciclo di vita dell'attività. Non ho bisogno di nessuna di queste funzionalità.

Aggiornamento 2

As pointed out by Reuben, Fragment.setRetainInstance(bool) ha sostituito il deprecato getLastNonConfigurationInstance(). A questo punto, sembra che l'opzione migliore sia quella di creare un frammento persistente non-UI usando setRetainInstance(true).

risposta

1

C'è un sacco di soluzioni a questo. Se non stai usando Fragments, l'opzione più semplice è sovrascrivere onRetainNonConfigurationInstance(). Non importa che l'API sia deprecata, è solo perché vogliono che tu usi Fragments (dove, per essere onesti, Fragment.setRetainInstance() rende questo intero problema il gioco da ragazzi che dovrebbe sempre essere stato). Questa API non andrà da nessuna parte per molto tempo.

Quello che mi piacerebbe fare è ignorare onRetainNonConfiguration() per restituire 'questo', cioè un riferimento all'istanza di attività morti, e poi copiare gli arbitri devi da onCreate(), vale a dire:

Object obj = getLastNonConfigurationInstance(); 
if (obj != null) { 
    MyActivity deadActivity = (MyActivity)obj; 
    this.foo = deadActivity.foo; 
    this.bar = deadActivity.bar; 
    ... 
} 

In alternativa si può utilizzare un servizio, ma personalmente non mi piace. Utile per fare roba cross-process, senza dubbio, ma per il resto sono una soluzione alla ricerca di un problema.

Infine, come punto generale dell'ordine è possibile inserire dati "globali" nel contesto dell'applicazione. Per fare ciò, sottoclassi android.app.Application e usa l'attributo android: name = "MyApp" > di android < nel tuo manifest. È utile anche mantenere un riferimento all'oggetto Application nei dati statici globali qui, in modo che AsyncTasks e altro codice context-less possano sempre accedere al contesto dell'applicazione senza dover passare i parametri di contesto in giro senza motivo.

class MyApp extends Application { 
... 

    public static MyApp context; 
    ... 

    @Override 
    public void onCreate() { 
     super.onCreate(); 

     context = this; 

     ... set up global data here ... 
    } 
... 
} 
+0

Vale la pena creare un singolo frammento per questa attività solo così posso usare 'setRetainInstance'? Il frammento "conservato" sarà in grado di ruotare e ridimensionarsi su un cambiamento di orientamento? Inoltre, [questa domanda SO] (http://stackoverflow.com/questions/4585627) mi ha insegnato che una sottoclasse di applicazioni NON è un modo sicuro per archiviare globals. In effetti, può portare a bachi che sono quasi impossibili da ricreare. – firyice

+0

Re. Frammento, sì. Vale la pena abbracciare il modo di fare frammenti prima e dopo. Ri-sottoclasse l'applicazione, qualcuno è confuso. Sì, lo stato dell'attività può essere bloccato dal sistema operativo mentre il processo viene interrotto, ma allora? Application.onCreate() viene eseguito ancora quando il processo/l'attività si riavvia/riprende. –

+0

E allora? Se il processo viene riavviato e Application.onCreate() viene chiamato, hai perso i dati in quelle variabili globali. L'unico modo che potrebbe essere ok è per le costanti. – firyice

8

vorrei risolvere questo problema utilizzando un Service che avrebbe gestito la connessione Bluetooth, e fare che il Servizio parlare di nuovo al vostro attività, come descritto nella this answer.

Quindi, è possibile utilizzare l'AsyncTask per mostrare semplicemente/nascondere un finestra di dialogo, e annulla/riavvia ASyncTasks sulla rotazione, che è fatto da Shelves Example come hai menzionato.

I servizi non sono poi così orribili e potrebbero essere lo strumento migliore per il tuo problema.

+0

Questa sembra una soluzione sicura. Tuttavia, questo mi colpisce anche come l'equivalente di guidare in un chiodo con un martello pneumatico. I servizi sembrano davvero destinati alla comunicazione con più processi. Sì, potrebbero essere utilizzati per alcune semplici attività all'interno di un processo, ma sembra troppo pesante per qualcosa che dovrebbe essere relativamente semplice. – firyice

+4

Non sono d'accordo. Volevo che la mia applicazione gestisse il backup di sfondo sul server: un semplice IntentService a 20 linee era la soluzione più semplice che potessi trovare. – Axarydax

2

Si può provare con lo singleton pattern che può gestire tutto e chiamare l'attività principale quando è necessario.

Quindi è un metodo statico ottenere un'istanza dell'oggetto MySingleton, la stessa istanza ogni volta che si chiama getInstance. Puoi "memorizzare" tutti gli oggetti bluetooth in esso contenuti, non sarà distrutto e accessibile da ogni attività.

public class MySingleton { 
    private static MySingleton instance; 
    public static MySingleton getInstance() { 
     if (null == instance) { 
      instance = new MySingleton(); 
     } 
    return instance; 
} 

    private MySingleton() { 
    } 
} 
+0

È a mia conoscenza che la classe Application, che è un singleton fornita da Android per ogni app, ha il proprio ciclo di vita. Anche in casi rari può essere distrutto senza che l'intera app venga distrutta. Intendi una classe singleton separata? Potresti fornire un esempio di codice di cosa stai parlando qui? – firyice

+0

Non l'ho provato per la connessione Bluetooth, ma una classe singleton non dovrebbe essere distrutta. Ci scusiamo per la formattazione, non sono un'esportazione SO, ma è solo una normale classe singleton in cui avresti tutto ciò che riguarda la connessione bluetooth. ' classe pubblica Singleton { istanza Singleton statica privata; statistico Singleton getInstance() { if (null == instance) { instance = new Singleton(); } restituisci istanza; } Singleton privato() {} } ' –

+0

Grazie per il chiarimento! Potresti aggiornare la tua risposta con il codice. Puoi indentare ogni riga con 4 spazi per renderla formattata come codice. – firyice

4

Non posizionare mai App Model (logica o dati) nei componenti Visual/UI. Come vedi, vanno e vengono e cambiano.

Luoghi da tenere roba non legati UI come raccolte di dati, collegamenti in diretta, fili ecc:

  • classe Application. Avvolge il ciclo di vita di tutti gli altri componenti. Quasi come un singleton globale. Puoi usarlo per la memorizzazione temporanea.

esempio:

public class App extends Application { 

    private static Beer sBeer; 

    public static void brbHoldMyBeer(Beer b){ 
    sBeer = b; 
    } 

    public static Beer imBackWheresMyBeer(){ 
    return sBeer; 
    } 

} 

Inoltre, avere un servizio di filo Executor statico nella classe Application, contribuirà a mantenere le attività in esecuzione.

  • Un servizio in background. A quali attività, frammenti ecc. Possono legare/separare e pubblicare comandi/richieste. Questo è il posto consigliato per i processi e i dati in esecuzione su tutta l'App.

  • Un frammento non visivo con setRetainInstance(true). Qui non visivo significa che è un frammento fittizio allegato all'Attività, ma non mostra alcuna vista. È usato solo come porta oggetti conservabile per un'attività. Questo è raccomandato per i processi e i dati a livello di attività.

+0

drink = imBackWheresMyBeer(); – Luis

+0

[La documentazione di Android consiglia di conservare gli oggetti durante le modifiche alla configurazione collegandoli a un frammento non permanente.] (Http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject) (Solo accumulo su riferimenti.) –

Problemi correlati