2012-02-20 11 views
12

Ho un'applicazione Android che è vincolante per un servizio persistente (una volta avviato con startService()).Legare/rilegare in modo pulito a un servizio in un'applicazione

Il servizio è parte integrante dell'applicazione e viene quindi utilizzato in quasi tutte le attività. Quindi voglio legare al servizio una sola volta (invece di legare/slegare in ogni attività) e mantenere l'associazione durante la vita della mia applicazione.

Ho esteso da Application e associo al servizio in Application#onCreate(). Tuttavia Ora ho il problema che non so quando esiste la mia applicazione in quanto Application#onTerminate() viene mai chiamato, vedi JavaDoc:

Questo metodo è per l'uso in ambienti di processo emulati. Non sarà mai essere chiamato su un dispositivo Android di produzione, in cui i processi vengono rimossi semplicemente uccidendoli; nessun codice utente (incluso questo callback) è eseguito quando lo si fa.

Quindi, come posso eliminare in modo pulito da un servizio associato in Applicazione?

risposta

12

Ho risolto questo problema contando i riferimenti all'associazione di servizio nello Application. Ogni Activity deve chiamare acquireBinding() nei loro metodi onCreate() e chiamare releaseBinding() in onDestroy(). Se il contatore di riferimento raggiunge lo zero, il legame viene rilasciato.

Ecco un esempio:

class MyApp extends Application { 
    private final AtomicInteger refCount = new AtomicInteger(); 
    private Binding binding; 

    @Override 
    public void onCreate() { 
     // create service binding here 
    } 

    public Binding acquireBinding() { 
     refCount.incrementAndGet(); 
     return binding; 
    } 

    public void releaseBinding() { 
     if (refCount.get() == 0 || refCount.decrementAndGet() == 0) { 
      // release binding 
     } 
    } 
} 

// Base Activity for all other Activities 
abstract class MyBaseActivity extend Activity { 
    protected MyApp app; 
    protected Binding binding; 

    @Override 
    public void onCreate(Bundle savedBundleState) { 
     super.onCreate(savedBundleState); 
     this.app = (MyApp) getApplication(); 
     this.binding = this.app.acquireBinding(); 
    } 

    @Override 
    public void onDestroy() { 
     super.onDestroy(); 
     this.app.releaseBinding(); 
    } 
} 
+0

In che modo il vostro servizio sopravvive alle rotazioni dello schermo? –

+1

Cosa intendi? Un servizio in Android non ha un'interfaccia utente quindi continua a essere in esecuzione. In questo caso il servizio è stato precedentemente avviato con 'context.startService()' ed è contrassegnato come appiccicoso, quindi viene eseguito finché non viene arrestato manualmente o si arresta automaticamente. –

+0

Capisco. Stavo pensando che il rilascio del binding avrebbe interrotto il servizio, ma non in questo caso di servizio ibrido. L'associazione viene rilasciata al fine di evitare una connessione di servizio trapelata. –

4

Dalla risposta di Sven:

Ho risolto questo problema contando i riferimenti al servizio vincolante l'applicazione. Ogni attività deve chiamare acquireBinding() nei loro metodi onCreate() e chiamare releaseBinding() in onDestroy(). Se il contatore di riferimento raggiunge lo zero, il binding è rilasciato.

Sono d'accordo, MA non dovresti farlo in onDestroy - che spesso non viene chiamato.

Invece ho suggeriamo le seguenti (in base al codice di esempio) ...

// Base Activity for all other Activities 
abstract class MyBaseActivity extend Activity { 
    protected MyApp app; 
    protected Binding binding; 

    @Override 
    public void onCreate(Bundle savedBundleState) { 
     super.onCreate(savedBundleState); 
     this.app = (MyApp) getApplication(); 
     this.binding = this.app.acquireBinding(); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 
     // Pre-HC, activity is killable after this. 
     if ((11 > Build.VERSION.SDK_INT) && (isFinishing())) 
      onFinishing(); 
    } 

    @Override 
    protected void onStop() { 
     super.onStop(); 
     if ((10 < Build.VERSION.SDK_INT) && (isFinishing())) 
      onFinishing(); 
    } 

    protected void onFinishing() { 
     // Do all activity clean-up here. 
     this.app.releaseBinding();   
    } 
} 

MA, il mio uso di isFinishing() è solo un pensiero - Io non sono certo che sia affidabile. Forse onPause/onStop viene chiamato con isFinishing() false, ma poi l'attività viene uccisa e il tuo rilascioBinding() non viene mai chiamato.

Se sbarazzarsi del controllo isFinishing Penso che avete bisogno di spostare la chiamata acquireBinding() da onCreate a onStart/onResume (a seconda della versione SDK), per garantire che il conteggio ref non viene incasinato.

Chi sapeva che il rilascio del servizio dell'app sarebbe stato così complicato!

+0

Grazie per il chiarimento. Darei un'occhiata a questo. Ho anche l'impressione che la mia soluzione non sia sempre affidabile. –

2

In questo caso lo scioglimento è assolutamente necessario? L'applicazione viene comunque uccisa. Ho provato a implementare un'applicazione di esempio eseguendo questa operazione senza disassemblare e sembra funzionare correttamente.

+0

Se non ci si disfa, si dovrebbero vedere errori di "connessione al servizio trapelato" nei registri. –