2013-04-23 5 views
14

Ich habe eine Android-App mit einigen globalen Zustand (einschließlich einige große SoundPool s), die aufräumen müssen, so folgende Antworten auf meine vorherige Frage, die ich versuche Handle dies mit einem Service.Wie bekomme ich einen Android "gebundenen" Service, um einen Neustart der Konfiguration zu überleben

Ich verwende derzeit einen gebunden Dienst, der jede Aktivität/onStart/onStop entbindet in bindet, und wenn alle Aktivitäten stoppen der Dienst wird ungebunden und onDestroy auf dem Dienst aufgerufen, ich die SoundPools ließ los.

Da der Aktivitätszyklus absichtlich überlappt (die neue Aktivität onStart wird ausgelöst, bevor die alte onStop auslöst), ist beim Navigieren zwischen den Aktivitäten immer mindestens eine Aktivität gebunden und der Dienst bleibt aktiv.

Wenn der Bildschirm jedoch gedreht wird, um einen Neustart der Konfiguration zu verursachen, ist der Dienst nicht gebunden und wird beendet, wenn die aktive Aktivität den Lebenszyklus des Konfigurationsneustarts durchläuft.

Wie kann ich das umgehen und den Dienst während des Neustarts aufrecht erhalten, während der Dienst nach dem Beenden der Anwendung trotzdem abstürzt?

+0

Ich werde irgendwann mehr daran arbeiten müssen. Ich vermeide es, wie die Pest zu binden, also habe ich kein Muster, das dein Problem angehen könnte, und anscheinend war der Rat, den ich dir gegeben habe, fehlerhaft. – CommonsWare

+0

Wie wäre es mit ein paar Bind/Unbind Funktionalität zu meiner 'Application' Instanz hinzufügen - erhöhen/dekrementieren einen internen Zähler und nur tatsächlich einmal binden/lösen? – cjn

+0

Es ist möglich, dass das funktioniert, obwohl es ein bisschen riskant ist (z. B. nicht behandelte Ausnahmen, die dazu führen, dass Sie nicht dekrementieren). – CommonsWare

Antwort

18

Ok, da sich das als besonders schwierige Frage herausgestellt hat, dachte ich, ich würde hier meine Lösung posten, falls irgendjemand etwas Ähnliches trifft.

Es gibt eindeutig mehrere Möglichkeiten, dies zu erreichen, aber das einfachste, das ich verwendet habe, ist einen "gestarteten" Dienst zu haben, der entscheidet, wann er sich selbst herunterfährt. Meine Aktivitäten binden/binden an den Dienst, und nach einer gewissen Zeitverzögerung (ich habe 1 Minute benutzt) schaltet sich der Dienst ab, wenn keine weiteren Aktivitäten gebunden sind - dies gilt, wenn der Benutzer die App nicht mehr verwendet, und wenn dies der Fall ist irgendwelche fatalen Aktivitätsfehler.

Der Shutdown-Timer wird innerhalb onUnbind() geplant und wird in onStartCommand(), onBind(), onRebind() abgebrochen. Wenn es ausgelöst wird, wird der Dienst sauber heruntergefahren, was wiederum eine Bereinigung des verwalteten Status im Dienst onDestroy() des Dienstes auslöst.

Mein Service Code ist wie folgt:

public class LocalStateService extends Service { 

    /** The binder to give to clients. */ 
    private final IBinder binder = new LocalStateBinder(); 

    /** Used for time-delayed shutdown. */ 
    private final Handler handler = new Handler(); 

    /** 
    * Called before starting or the first binding. 
    */ 
    @Override 
    public void onCreate() { 
     // initialise state... 
    } 

    /** 
    * Called when this service is explicitly started. 
    * @param intent The intent passed on starting, unused 
    * @param flags  Startup flags, unused 
    * @param startId Identifies each start request 
    * @return Desired restart behaviour 
    */ 
    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     cancelShutdown(); 

     // if killed, we would like Android to restart this service, but don't bother re-delivering 
     // the original intent used to start the service 
     return START_STICKY; 
    } 

    /** 
    * Called when the first client binds. 
    * @param intent  The intent passed on binding 
    * @return The binding to use 
    */ 
    @Override 
    public IBinder onBind(Intent intent) { 
     cancelShutdown(); 
     return binder; 
    } 

    /** 
    * Called when the first of previous clients re-binds. 
    * @param intent  The intent passed on binding 
    */ 
    @Override 
    public void onRebind(Intent intent) { 
     cancelShutdown(); 
    } 

    /** 
    * Called when all clients have unbound. 
    * @param intent  The first intent originally passed on binding 
    * @return Whether this service should be notified of rebinding 
    */ 
    @Override 
    public boolean onUnbind(Intent intent) { 

     // post a callback to be run in 1 minute 
     handler.postDelayed(delayedShutdown, 1000L * 60); 

     // we do want onRebind called when clients return 
     return true; 
    } 


    @Override 
    public void onDestroy() { 
     // state cleanup... 
    } 

    private Runnable delayedShutdown = new Runnable() { 

     @Override 
     public void run() { 
      LocalStateService.this.stopSelf(); 
     } 

    }; 

    /** 
    * Cancel any shutdown timer that may have been set. 
    */ 
    private void cancelShutdown() { 
     // remove any shutdown callbacks registered 
     handler.removeCallbacks(delayedShutdown); 
    } 
} 

Statt dies zu tun aus meiner Application, meine Haupttätigkeit startService(..) in onCreate() da dies erfordert sowohl für die Erstinbetriebnahme arbeiten und wenn der Benutzer geht ein auf die Verwendung pausierte App (wo der Dienst sich vielleicht entschieden hat, sich selbst zu schließen).

Jede Aktivität bindet und löst dann wie normal.

Ich fand, dass:

  • Wenn zwischen den Aktivitäten der Navigation, wird kein Service Rückruf gefeuert. Da die Aktivität Lebenszyklus überlappt, sind diese sekundären bind/entbindet

  • Wenn eine Aktivität neu gestartet wird (zB Bildschirm-Rotation), wird der Dienst eine onUnbind() dann eine onRebind() Anruf

  • Bei Anhalten der App (zB Presse zu Hause aus Haupttätigkeit) oder beim Abschluss (z. B.drücken Sie zurück von der Hauptaktivität), der Dienst wird onUnbind() dann wird der Timer ausgelöst.

+1

Dies ist eine ziemlich erfrischende R & D. Vielen Dank . –

+1

Haben Sie darüber nachgedacht, startService (self) von onBind() aus aufzurufen? –

+0

Warum cancelShutdown in onBind aufrufen? Wie kann ein Herunterfahren geplant werden, bevor die erste Aktivität an den Dienst gebunden wird? –

Verwandte Themen