2012-02-20 8 views
12

Tengo una aplicación de Android que se vincula a un servicio persistente (una vez que se inició con startService()).Encuadernar/desvincular de forma limpia a un servicio en una aplicación

El servicio es una parte integral de la aplicación y, por lo tanto, se utiliza en casi todas las actividades. Por lo tanto, quiero vincularme al servicio solo una vez (en lugar de enlazar/desvincular en cada actividad) y mantener el enlace durante el tiempo de vida de mi aplicación.

He extendido desde Application y acepto el servicio en Application#onCreate(). Sin embargo ahora tengo el problema de que no sé cuando existe mi solicitud ya Application#onTerminate() Nunca se llama, ver JavaDoc:

Este método es para su uso en entornos de procesos emulados. Nunca se llamará a en un dispositivo Android de producción, donde los procesos se eliminan simplemente matándolos; ningún código de usuario (incluida esta devolución de llamada) es ejecutado al hacerlo.

Entonces, ¿cómo debo desvincularme limpiamente de un servicio vinculado en la aplicación?

Respuesta

12

He resuelto este problema contando las referencias al enlace de servicio en el Application. Cada Activity tiene que llamar al acquireBinding() en sus métodos onCreate() y llamar al releaseBinding() en onDestroy(). Si el contador de referencia llega a cero, se libera el enlace.

He aquí un ejemplo:

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

¿Cómo sobrevive su servicio a las rotaciones de pantalla? –

+1

¿Qué quieres decir? Un servicio en Android no tiene una IU, así que sigue funcionando. En este caso, el Servicio se inició previamente con 'context.startService()' y se marca adhesivo para que se ejecute hasta que se detenga manualmente o se detenga. –

+0

Ya veo. Estaba pensando que liberar el enlace podría detener el servicio, pero no en este caso de servicio híbrido. El enlace se libera para evitar una conexión de servicio filtrada. –

4

De la respuesta de Sven:

He resuelto este problema mediante recuento de las referencias al servicio vinculante en la Solicitud. Cada actividad tiene que llamar al acquireBinding() en sus métodos onCreate() y llamar a releaseBinding() en onDestroy(). Si el contador de referencia llega a cero, se libera el enlace .

Estoy de acuerdo, PERO no deberías hacerlo en onDesy, que a menudo no se llamará.

En lugar de eso sugieren lo siguiente (basado en el código de ejemplo) ...

// 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();   
    } 
} 

PERO, mi uso de isFinishing() es sólo un pensamiento - No estoy seguro de que es confiable. Quizás onPause/onStop se llame con isFinishing() falso, pero luego se mata la actividad, y nunca se llama a su releasBinding().

Si se deshace de la comprobación de isFinishing, creo que debe mover la llamada de acquireBinding() de onCreate a onStart/onResume (según la versión de sdk), para asegurarse de que su recuento de ref no se estropee.

¡Quién sabe si liberar el servicio de su aplicación sería tan complicado!

+0

Gracias por la aclaración. Voy a echar un vistazo a esto. También tengo la impresión de que mi solución no es confiable todo el tiempo. –

2

¿Es necesario desvincular en este caso? La aplicación se mata de todos modos. Intenté implementar una aplicación de muestra sin desconectar y parece funcionar correctamente.

+0

Si no lo desvincula, debería ver los errores de "conexión de servicio filtrada" en los registros. –

Cuestiones relacionadas