12

Auf Android habe ich eine Activity namens FirstActivity, die eine Service namens MyService startet, um Netzwerk-Sachen im Hintergrund zu tun. Die Activity und die Service kommunizieren ständig miteinander durch Aufruf von Methoden.Aktivität mit lang anhaltenden Service im Hintergrund, die nicht getötet wird

Jetzt, wenn der Benutzer FirstActivity-SecondActivity navigiert, sollte der Hintergrunddienst nicht getötet oder neu erstellt werden, aber am Leben gehalten und zu SecondActivity geführt, die nun die mit dem Dienst zu kommunizieren ist.

Mit anderen Worten wird die Service so lange laufen, wie einer der beiden Activity s läuft, und es sollte nicht aufhören, während der Benutzer zwischen den beiden Activity s navigiert.

Eines der Activity s wird immer im Vordergrund stehen und während dieser Zeit sollte der Dienst (optimal) niemals abgetötet werden. Ich denke, das sollte kein Problem sein, denn einer der beiden Activity s ist immer aktiv und somit weiß Android, dass der Dienst wichtig ist und nicht etwas, das getötet werden muss.

(Wenn es keine Möglichkeit gab, Android zu verhindern, dass das Töten und neu zu erstellen, den Service von Zeit zu Zeit, ich würde einen Weg brauchen den vollen Zustand des Dienstes ordnungsgemäß wiederherzustellen.)

Zusammengefasst sollte die Service die gleiche Lebensdauer wie die beiden Activity s "kombiniert" haben. Es sollte mit dem ersten von ihnen beginnen und nicht aufhören, bevor beide zerstört wurden.

Also ist der folgende Code richtig für diese Einrichtung und Ziele?

public class MyService extends Service { 

    public class LocalBinder extends Binder { 
     public MyService getService() { 
      return MyService.this; 
     } 
    } 

    ... 

} 

public class FirstActivity extends Activity { 

    private MyService mMyService; 

    private ServiceConnection mMainServiceConnection = new ServiceConnection() { 

     @Override 
     public void onServiceConnected(ComponentName className, IBinder service) { 
      MyService mainService = ((LocalBinder) service).getService(); 
      mMyService = mainService; 
      mMyService.setCallback(FirstActivity.this); 
     } 

     @Override 
     public void onServiceDisconnected(ComponentName className) { 
      mMyService = null; 
     } 
    }; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     ... 
     startService(new Intent(FirstActivity.this, MyService.class)); 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     bindService(new Intent(FirstActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 
     if (mMainServiceConnection != null) { 
      unbindService(mMainServiceConnection); 
     } 

     if (mMyService != null) { 
      mMyService.setCallback(null); 
     } 

     if (!isUserMovingToSecondActivity) { 
      stopService(new Intent(FirstActivity.this, MyService.class)); 
     } 
    } 

    @Override 
    public void onBackPressed() { 
     stopService(new Intent(FirstActivity.this, MyService.class)); 
     super.onBackPressed(); 
    } 

    ... 

} 

public class SecondActivity extends Activity { 

    private MyService mMyService; 

    private ServiceConnection mMainServiceConnection = new ServiceConnection() { 

     @Override 
     public void onServiceConnected(ComponentName className, IBinder service) { 
      MyService mainService = ((LocalBinder) service).getService(); 
      mMyService = mainService; 
      mMyService.setCallback(SecondActivity.this); 
     } 

     @Override 
     public void onServiceDisconnected(ComponentName className) { 
      mMyService = null; 
     } 
    }; 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     bindService(new Intent(SecondActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 
     if (mMainServiceConnection != null) { 
      unbindService(mMainServiceConnection); 
     } 
    } 

    @Override 
    protected void onDestroy() { 
     ... 
     stopService(new Intent(SecondActivity.this, MyService.class)); 
    } 

    ... 

} 

Ist dies der beste Weg, um einen lang anhaltenden Dienst im Hintergrund des Activity s zu gewährleisten, die getötet oder neu erstellt werden nicht?

Was ist mit Context.BIND_AUTO_CREATE? Ist es richtig, diese Flagge hier zu setzen? Was ist mit Context.BIND_ADJUST_WITH_ACTIVITY und Context.BIND_WAIVE_PRIORITY - brauche ich diese?

+0

Haben Sie diesen Code ausprobiert oder fragen Sie vor dem Versuch? Wenn Sie es versucht haben, haben Sie irgendwelche Fehler bekommen? Hat es sich nicht so verhalten, wie du es erwartet hast? Wir werden eine viel produktivere Diskussion und eine nützlichere Antwort für die Gemeinschaft und für dich selbst haben, wenn du es tatsächlich ausprobierst und dann nach der Lösung eines konkreten Problems fragst – DallaRosa

+0

@DallaRosa Ja, natürlich habe ich das versucht.Dies ist eine allgemeine Frage, wie eine Serviceklasse und die entsprechenden 'Activity'-Klassen entworfen werden, damit der Service so lange wie möglich dauert und nicht getötet wird. Benutzer haben gemeldet, dass die von diesem Dienst bereitgestellten Funktionen nicht mehr in einer der "Activity" -Klassen verfügbar sind. Und ich (a) finde es schwierig zu debuggen, wenn das System diesen Service killt und (b) würde gerne etwas über Best Practices oder Verbesserungen erfahren, die auf diesen Service für die genau definierten Ziele und Anforderungen angewendet werden sollten. – caw

+0

Ich kann viele Probleme in Ihrer Verwaltung des 'Service' sehen. Sie müssen es nur stoppen, wenn die erste 'Aktivität' zerstört ist, oder von der Bindung abhängen und diese richtig verwalten. Es gibt einige allgemeine Anweisungen dazu in den Android-Startbüchern: https://books.google.com.pk/books?id=mRGrCQbqHkoC&pg=PA399 – corsair992

Antwort

7

(Vielen Dank an @ corsair992 für seine nützlichen Hinweise!)


Wenn die Aktivitäten immer in dieser Reihenfolge genannt werden (dh FirstActivity beginnt SecondActivity, und nie anders herum, dann . Sie sollten grundsätzlich Versuch den Dienst des Lebenszyklus FirstActivity ‚s-Lebenszyklus zu„binden“

Im allgemeinen (siehe Einsprüche später) bedeutet dies:

  • Rufen Sie startService() in FirstActivity.onCreate().
  • Rufen Sie stopService() in FirstActivity.onDestroy().
  • Rufen Sie bindService()/unbindService() in den onStart()/onStop() Methoden beider Aktivitäten auf (um Zugriff auf das Binder-Objekt zu erhalten und Methoden aufzurufen).

Dienst gestartet diese Weise am Leben sein wird, bis stopService()und jeder Kunde entbindet genannt wird, sehen Managing the Lifecycle of a Service:

Diese beiden Pfade nicht vollständig getrennt sind. Das heißt, Sie können an einen Dienst binden, der bereits mit startService() gestartet wurde. (...) In solchen Fällen stoppt stopService() oder stopSelf() den Dienst nicht wirklich, bis alle Clients unbind haben.

und:

Wenn der letzte Client aus dem Dienst entbindet, zerstört das System den Service (es sei denn, der Dienst auch von Startservice() gestartet wurde).

Mit dieser grundlegenden Strategie wird der Dienst so lange leben, wie FirstActivity herum ist (d. H. Es wird nicht zerstört). Es bleibt jedoch ein wichtiger Punkt: Im Falle einer Konfigurationsänderung (z. B. einer Bildschirmdrehung), die nicht explizit behandelt wird, wird die Aktivität neu gestartet und der Dienst wird zerstört (da wir stopService() in onDestroy() aufrufen) .

Um dies zu verhindern, können Sie isChangingConfigurations() überprüfen, bevor Sie den Dienst tatsächlich gestoppt (da ein onDestroy() Rückruf wegen auftretenden diesem Grund bedeutet, dass, obwohl diese bestimmte Instanz der Aktivität zerstört wird, wird es danach neu erstellt werden.

daher wäre die vollständige Lösung so etwas wie:

public class FirstActivity extends Activity 
{ 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     startService(new Intent(this, MyService.class)); 
    } 

    private ServiceConnection mServiceConnection = new ServiceConnection() { ... } 

    @Override 
    protected void onStart() { 
     super.onStart(); 
     bindService(new Intent(this, MyService.class), mServiceConnection, Context.BIND_AUTO_CREATE); 
    } 

    @Override 
    protected void onStop() { 
     unbindService(mServiceConnection); 
     super.onStop(); 
    } 

    @Override 
    protected void onDestroy() { 
     if (!isChangingConfigurations()) 
      stopService(new Intent(this, MyService.class)); 

     super.onDestroy(); 
    } 

Während SecondActivity würde nur die onStart()/onStop() Methoden implementieren (in der gleichen Weg).


Ein paar Notizen über Ihre spezielle Implementierung:

  • Es ist nicht notwendig onBackPressed(), außer Kraft zu setzen, da, wenn die Aktivität aufgerufen werden die notwendigen Lebenszyklus Methoden zerstört werden (plus, könnte es ohne fertig sein Drücken Sie die Zurück - Taste, zum Beispiel wenn Sie finish() aufrufen.
  • Wenn Sie den Dienst in onDestroy() anstelle von onPause() stoppen, ersparen Sie sich die Suche nach isUserMovingToSecondActivity.
+0

Danke! (1) Zu unserem Zweck können Sie 'onStart()'/'onStop()' und 'onResume()'/'onPause()' synonym verwenden, nicht wahr? Ansonsten erscheint das zweite Methodenpaar überlegen, weil es * garantiert * heißt, im Gegensatz zum ersten Paar, oder? (2) Macht der Check für 'isChangingConfigurations()' wirklich einen Unterschied? Wenn 'FirstActivity' zerstört wird, verlieren wir die Membervariable' mMyService' trotzdem und 'onCreate (...)' und 'onStart()' werden erneut aufgerufen (was auch bedeutet, dass Sie zweimal binden können). Und in "SecondActivity" verlieren wir auf jeden Fall die Verbindung. – caw

+1

@MarcoW. (1) 'onStop()' kann nicht ankommen, aber AFAIK, das ist nur, wenn die App getötet wird, in diesem Fall wird der Dienst auch. (2) Sie werden die lokale Bindung verlieren, aber der Service wird nicht wirklich zerstört und dann neu erstellt, wenn Sie erneut binden (wenn Sie also einen laufenden Prozess haben, wird er immer noch "da sein"). Es wird jedoch sein, wenn Sie 'stopService()' aufrufen. Ich nahm an, du wolltest den Dienststatus bei Rotation halten. – matiash

+0

Betrachtet man (2), wenn 'onStop()' in 'SecondActivity' aufgerufen wird, wird wahrscheinlich der' Service' beendet (weil keine 'Activity' mehr an ihn bindet und alle' Activity's, die 'startService (...) '' 'stopService (...)') aufgerufen haben, bevor Sie nach der Konfigurationsänderung in 'onStart()' erneut binden können. Liege ich falsch? – caw

Verwandte Themen