Kommunikation von Services untereinander und mit der Oberfläche?

  • Antworten:9
  • OffenNicht stickyNicht beantwortet

20.06.2012 13:56:32

Hallo,

es geht um Folgendes:

Ich programmiere mir gerade eine Fahrradcomputer App.

Diese soll zunächst diese Funktionen bieten:

- Tachoanzeige mit aktueller Geschwindigkeit, Tageskilometer, Durchschnitt usw.
- Maps-Ansicht mit Anzeige der aktuellen Position
- Tracking History Funktion. Es werden alle Fahrtrouten aufgezeichnet und
man kann diese jederzeit abrufen
- Anzeige von GPS Status Informationen

Ich habe diese Funktionen jeweils in einer eigener Activity realisiert,
sie sind über einen Hauptbildschirm aufrufbar.

Die Oberflächen sind weitestgehend umgesetzt, jetzt geht es
an die programmtechnische Umsetzung der Funktionen.
Die MapView habe ich bereits integriert und die GPS Status Infos
werden auch in Echtzeit angezeigt.

Nun bin ich auf eine Problematik gestoßen, bei der ich nicht so recht weiterkomme.
Der LocationManager samt seiner Funktionen (+Konvertierungsmethoden etc.) wird ja
in mehreren Activities benötigt. Außerdem wird ein XML Writer/Reader (gpx Format)
für die Trackingfunktion implementiert werden müssen.
Diese (eigentlich) Hintergrundprozesse würde ich in einen oder mehrere Services
und damit GUI unabhängige Threads auslagern. Ist dieser Ansatz schonmal richtig?
Falls ja kann ich mir noch nicht so richtig vorstellen, wie der Austausch mit
den einzelnen Activities von statten gehen soll?! Wird dies mit dem Intentsystem
umgesetzt oder gar BroadcastReceivern? Oder ist das nicht geeignet dafür und ich
muss die Interprozesskommunikation von Java (hat erstmal nichts mit Android zutun)
verwenden?

Für einen Denkanstoß wäre ich sehr dankbar, da ich im da Moment etwas im Dunkeln tappe
und auch den richtigen Ansatz wählen möchte.

Vielen Dank schonmal!

Gelöschter Account

20.06.2012 16:02:46

Korrekt.

Lagere den LocationManager in einen Service aus. Dieser kann selbständig die Daten verarbeiten (Datenbank, XML Writer, ...). Gleichzeitig sendet der Service die Location Updates per Broadcast an "interessierte" Activities aus. Die Activities wiederum können dem Service per Broadcast "Befehle" erteilen (Starten, Stoppen, Pausieren, Fortführen, ...).

Du benötigst also pro "interessierte" Activity einen BroadcastReceiver sowie einen im Service.

Das klappt hervorragend. Achte aber bitte darauf den "public int onStartCommand(Intent intent, int flags, int startId)" im Service mit START_REDELIVER_INTENT zu beenden. Nur so hast Du die Garantie das bei einem systembedingten Abschuss der Service mit den selben Daten wieder neu gestartet wird. Ob Du wieder aufsetzen musst merkst Du dann im onStartCommand() mit "if ((flags & START_FLAG_REDELIVERY) == 1)".

21.06.2012 13:53:35

Super, vielen Dank! :D Auch das mit der Wiederaufnahme des Trackings nach einem evtl. Absturz ist ein guter Hinweis. Sowas findet man in der Literatur nirgends.

Ich habe es jetzt mal ausprobiert, indem ich den Broadcast-Receiver als innere Klasse der Activityimplementiert habe. Der Service versendet einen benutzerdefinierten Intent, auf den ich dann filtere.
Ist das so ein guter Weg oder sollte ich die BroadcastReceiver als eigene Klassen erzeugen?
Dann stellt sich mir aber die Frage, wie die Kommunikation zur jeweiligen Activity von statten gehen soll. Dann bräuchte ich ja wohl nochmals einen Intent, was umständlich wäre.

Außerdem müsste ich noch wissen, ob ich die ausgelagerten Funktionen XML Read/Write und LocationFunktionen in einen gemeinsamen Service packen soll oder das Ganze in zweien getrennt halte?

Gelöschter Account

21.06.2012 15:15:17

Fragen über Fragen.

Mach in den Service und in jede "interessierte" Activity einen Inner Class BroadcastReceiver (die heißen bei mir immer MyBroadcastReceiver - dann lässt sich einfacher kopieren). Bei mir enthalten die Intents immer eine ACTION deren Werte ich im jeweiligen BroadcastReceiver final static deklariere. Diese Actions werden dann gefiltert und enthalten, je nach Action, unterschiedliche weitere Daten.

Was ist an einem Intent umständlich? In meinem Tankbuch kann der Benutzer zwischen verschiedenen Sichten umschalten (Tacho, Map, etc..). Ich schicke den gleichen Intent halt an alle.

1Intent intent = new Intent();
2 intent.putExtra("LATITUDE", location.getLatitude());
3 intent.putExtra("LONGITUDE", location.getLongitude());
4 intent.putExtra("SPEED", location.getSpeed());
5 // ...
6 intent.setAction(EineAktivity.MyBroadcastReceiver.BROADCASTRECEIVER);
7 sendBroadcast(intent);
8
9 intent.setAction(EineWeitereActivity.MyBroadcastReceiver.BROADCASTRECEIVER);
10 sendBroadcast(intent);

Zum Auslagern von Funktionen: Man kann es auch übertreiben. Ich komme aus der Spaghetti-Programmier-Welt (vor fast 35 Jahren mit diversen Assembler Sprachen angefangen). Am Beispiel des BroadcastReceivers siehst Du wo bei mir die Grenzen sind. Natürlich werden Klassen ausgelagert - aber nicht um es strikt nach Lehre zu machen sondern um es praktisch zu halten. Ich habe OO halt nie richtig gelernt ;-)

— geändert am 21.06.2012 15:20:31

21.06.2012 15:49:40

Harald Wilhelm
Mach in den Service und in jede "interessierte" Activity einen Inner Class BroadcastReceiver (die heißen bei mir immer MyBroadcastReceiver - dann lässt sich einfacher kopieren). Bei mir enthalten die Intents immer eine ACTION deren Werte ich im jeweiligen BroadcastReceiver final static deklariere. Diese Actions werden dann gefiltert und enthalten, je nach Action, unterschiedliche weitere Daten.
Jup, aber genau das mach ich ja auch. :bashful: Das meinte ich mit innerer Klasse. Wollte nur wissen, ob dies die bessere Lösung ist (anstatt den BroadcastReceiver in eine eigene abgeleitete Klasse einer neuen Datei zu verlagern)

Harald Wilhelm
Was ist an einem Intent umständlich? In meinem Tankbuch kann der Benutzer zwischen verschiedenen Sichten umschalten (Tacho, Map, etc..). Ich schicke den gleichen Intent halt an alle.
Prinzipiell spricht ja nichts dagegen, aber ich finde den Übergabeprozess dann doch etwas unnötig verlängert, wenn man macht:
- Activity ruft Service über Intent auf
- Service sendet im LocationChanged ein BroadcastIntent
- Broadcast empfängt BroadcastIntent
- Broadcast sendet Intents mit den Daten an alle betroffenen Activities
- Activities empfangen Intent und führen nun endlich ihre Oberflächenupdates aus
Ist das wirklich ein guter Weg?

Gelöschter Account

21.06.2012 19:50:36

Mit der von mir geschilderten Methode (die in zwei offiziellen Apps von mir eingebaut ist sowie in ein paar nicht offiziellen) fahre ich regelmäßig durch Deutschland, lasse mir die Strecke aufzeichnen und in der Datenbank speichern. Parallel läuft die Navigon Navi App - und das auf meinem 256MB uralt Google Nexus One von 2008 oder so.

Ich habe Dir lediglich geschildert womit ich in den vergangenen Jahren sehr gut gefahren bin. Es ist Dir unbenommen nach besseren Methoden zu suchen - und die wird es sicherlich geben.

Das habe ich im Übrigen nicht verstanden:


- Broadcast empfängt BroadcastIntent
- Broadcast sendet Intents mit den Daten an alle betroffenen Activities

21.06.2012 21:09:27

Harald Wilhelm
Mit der von mir geschilderten Methode (die in zwei offiziellen Apps von mir eingebaut ist sowie in ein paar nicht offiziellen) fahre ich regelmäßig durch Deutschland, lasse mir die Strecke aufzeichnen und in der Datenbank speichern. ......
Ok ok, hast mich ja überzeugt. :grin:


Harald Wilhelm

Das habe ich im Übrigen nicht verstanden:
- Broadcast empfängt BroadcastIntent
- Broadcast sendet Intents mit den Daten an alle betroffenen Activities

Naja, ich gehe bei diesem Konstrukt eben davon aus, dass die Broadcastreceiver nicht als innere Klassen der Activities realisiert sind, sondern
direkt als eigene Klassen, die von Broadcastreceiver ableiten.
Dann muss ja irgendwie die Kommunikation zwischen den Broadcastreceiverklassen und den Activities (auch eigene Klassen) hergestellt werden - und das geht doch nur über Intents oder?

Kleine Umformulierung:
- Broadcastreceiver empfängt BroadcastIntent vom Service und reagiert darauf in seiner OnReceive()-Methode
- Broadcastreceiver sendet in der OnReceive()-Methode Intents mit den Daten an alle betroffenen Activities


In diesem Fall sind die Klassen zB. so:
1HauptmenüActivity
2GPSService.java
3TachoActivity.java
4TachoBroadcastReceiver.java
5GPSInfoActivity.java
6GPSInfoBroadcastReceiver.java

— geändert am 21.06.2012 21:10:24

Gelöschter Account

21.06.2012 21:50:30

... und warum genau willst Du den BroadcastReceiver nicht als Inner Class in der Activity?

Im Paket der Android Developer Docs von Google ist eine Seite zum Thema Performance. Dort wird das Thema Inner Classes explizit besprochen. Die Kombination Inner Class, verzicht auf Getter/Setter sowie Package Variablen macht das Konstrukt kleiner und schneller.

Zweite Halbzeit geht los - wenn Dus nicht findest frag noch mal.

21.06.2012 22:24:06

Harald Wilhelm
... und warum genau willst Du den BroadcastReceiver nicht als Inner Class in der Activity?

Irgendwie finde ich diese Art zu implementieren nicht "so schön". Es macht den Code unübersichtlich und entspricht nicht dem objektorientierten Kapselung. Aber wie du schon erwähnt hast, ist es wohl bei BroadcastReceivern nicht angemessen, diese als eigene vollwertige Klasse auszulagern.
Wie ich schon sagte, fände ich das zusätzliche Senden eines Intents zu der Acitvity wiederum zu holprig. Man macht es sich damit ja nur unnötig schwer und bläst den Code dadurch nur auf. Somit werde ich wohl die Implementierung als inneren Klassen wählen.
Mich hat es im Prinzip auch nur mal interessiert, wie du diese Alternative findest.


Im Paket der Android Developer Docs von Google ist eine Seite zum Thema Performance. ...

Doch, ich denke ich hab's gefunden?
http://developer.android.com/guide/practices/performance.html#package_inner
Sieht aber auf den ersten Blick so aus, als würde hier gegen Inner Classes argumentiert.

Übrigens danke für diesen Tipp mit der Performance Sparte. Kannte ich nicht und ist sehr interessant! :D

Gelöschter Account

21.06.2012 23:22:00

Ich meine den Teil mit den private Inner Classes. Du hast Zugriff auf die Objekte der surrounding Activity. Das geht aber nur bei BroadcastReceivern die Du selbst startest. Die vom Manifest aktivierten können nicht private sein.

Manchmal muß man sich von "schön" verabschieden (Form follows function).