HowTo Sensor-App Entwicklung

  • Antworten:22
  • OffenNicht stickyNicht beantwortet
  • Forum-Beiträge: 13

17.11.2018, 15:26:09 via Website

Hallo Android-Programmierer,

ich bin auf der Suche nach Unterstützung bei der grundlegenden Erstellung einer Test-App. Mir fehlt die Erfahrung wie eine gut strukturierte Android-App aufzubauen ist.

Ich möchte eine Test-App aus einem mir vertrauten Themengebiet bauen, welche es schon x-fach gibt, ich aber verstehen möchte wie die Abläufe sind und auf was man bei der Entwicklung achten sollte sowie welche Tools einem das Abtesten erleichtern.

Was soll die App machen?
Durch drücken eines Button sollen alle verfügbaren Sensorsignale und GPS-Signale im Hintergrund abgefragt werden und mit einem Zeitstempel versehen an die UI und eine Tempdatei, Datenbank oder so in einen Ringspeicher zu Aufzeichnung und Weiterverarbeitung in einer sinnvollen Struktur weitergegeben werden. Die Anzeige soll erstmal nur in Zahlenform darstellen, aber auch beliebige weitere Activities sollen auf diesen zentralen Datenstream zugreifen können. Um die Bedienbarkeit robust zu bekommen möchte ich das Thema Threading hier mit einbeziehen und die verschiedenen Activities/Services sinnvoll aufteilen.

Wer könnte mir hier beginnend mit einem grundlegenden, cleanen Architekturaufbau mit Tipps und Link-Empfehlungen unter die Arme greifen, so dass ich am Ende verstanden habe wie professionelles Programmieren abläuft und eine saubere OOP-Struktur aussieht?

Als Ingenieur im Automotive-Bereich komme ich eher aus einer funktionsorientierten Programmierdenke (Nehme Eingangssignal x, manipuliere dieses und gebe es als Ausgangssignal an die nächste Funktion weiter), unterstützt im Wesentlichen durch grafikunterstützte Tools wie Matlab/Simulink/Stateflow. 

Seit mehreren Jahren versuche ich in die OOP-Denke reinzukommen. Dafür habe ich mir die JAVA-Grundkenntnisse über einschlägige Literatur, Tutorials und VHS-Kurse angeeignet, mich durch die AndroidDevelopers-Seiten geforscht und mehrere Anläufe in Android Studio gewagt. Aktuell fehlt mir die Möglichkeit neben dem einseitigen erklärt bekommen auch mal Fragen stellen zu können um nicht am Anfang schon in eine Sackgasse abzubiegen.

Ich bin gespannt ob sich hier jemand findet oder man evtl. diesen Post als Beispiel-Tutorial gemeinsam aufbaut... Antworten gerne hier oder nach Belieben per PN.

Erste konkrete Fragen sind:

Wie sieht für meine Anwendung eine sinnvolle Aufteilung in Activities und Services aus und welche Multi-Threading-Methode wäre zu empfehlen (ich würde zu Async-Tasks tendieren und die Sensorwerte an den UI-Thread zentral übergeben).

Viele Grüße

Oliver

Diskutiere mit!
Pascal P.
  • Mod
  • Blogger
  • Forum-Beiträge: 10.206

17.11.2018, 15:55:13 via Website

Hallo Oliver,

Herzlich wilkommen hier im Forum :)


Eine komplette Anleitung, wie ein Tutorial können wir dir hier leider nicht geben.
Das würde verutlich den Zeitrahmen sprengen. ;)

Konkrete Fragen sind aber imme gern gesehen, daher hier mal meine Meinung dazu:

Zum Abrufen von Sensordaten würde ich einen Service programmieren, der Daten (aller Art) entgegenimmt bzw abruft und diese dann per Event oder auf direkte Abfrage zur Verfügung stellt.
Ein AsyncTask hat den Nachteil, dass er nur für einzelne kurze Tasks im Hintergrund ausgelegt ist, zudem kannst du auf dessen Instanz aus einer Aktivity auch schlecht zurückgreifen, um z.b. einen Event zu abonnieren.

Am besten du liest dich mal in Sensorauswertung und Services ein:
http://devdeeds.com/android-location-tracking-in-background-service/

— geändert am 17.11.2018, 15:55:54

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

17.11.2018, 17:24:54 via Website

Hallo Pascal,

danke für deine Erläuterungen. Wie schaut es denn mit der Festlegung des Threadhandlings aus? Der Link auf DevDeeds scheint sich nur auf die Geodaten in einem Thread zu konzentrieren und beendet den Service wenn die Activity geschlossen wird. Ich würde die Aufzeichnung auch gerne weiterlaufen lassen wenn die Activity verlassen wird und erst durch erneuten ButtonPush stoppen. Und das ohne die UI zu beeinträchtigen.

Wie erarbeitet man sich die Software-Architektur bevor man losprogrammiert? Wenn ich jetzt den DevDeeds-Umfang übernehme und dann die Sensorerfassung aus einem anderen Beitrag, dann werde ich doch eher in einen bunten Code-Mix ohne vorher definierte Struktur enden, oder?

Hilfreich?
Diskutiere mit!
Pascal P.
  • Mod
  • Blogger
  • Forum-Beiträge: 10.206

17.11.2018, 19:13:15 via Website

Wenn du dir das vorher erarbeiten willst, musst du die Funktionsweisen von Services etc. im Detail kennen.
Da muss man schon ziemlich eingearbeitet sein, ohne das geht es nicht. (Ist auch nicht einfach, da Android ein Background Service nach lust und laune beenden kann und du kannst nicht immer etwas dagegen tun, z.b. bei Samsung devices und der Akkuoptimierung)
Ich kann dir das aus dem Stehgreif auch nicht sagen (obwohl ich auch schon Jahre Java insb. Android programmiere, allerdings weniger mit Services etc.)

Wenn du den Code blind übernimmst, hast du bunten Code Mix.
Was ich in solchen Situationen gerne mache:
Ich baue mir einen Prototyp, der den gewünschen Funktionsumfang (mehr oder weniger funktionierend) enthält, einfach um mich mit den Technologien vertraut zu machen.
Danach kann ich mir genauer überlegen, wie ich das Strukturieren will, denn dann habe ich Kenntnis mit welchen Konstrukten in Android ich es zutun haben und wie diese (vtl auch nur grob) funktionieren.
Manchen Code des Prototyps kann ich wiederverwenden, manchen aber auch nicht...

Natürlich kann man sich auch nur mithilfe der Dok einlesen und verstehen, für mich ist so ein Testprojekt allerdings vielversprechender und evtl. der Lerneffekt auch höher (da du einfach mit Code "spielen" kannst ohne eine genaue Struktur einhalten zu müssen).
Zudem geht das in der Form auch nur, wenn du die nötige Zeit dazu hast.

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

17.11.2018, 20:20:30 via Website

Ok. Danke. Welches Multithreading-Konzept sollte ich für meine Anwendung in den Fokus nehmen? Ich würde wie erwähnt gerne im Hintergrund stumpf einen (oder mehrere) Services permanent laufen lassen und den Ringspeicher befüllen. Die UI soll dann nur auf den Ringspeicher zugreifen. Macht das Sinn?

Liege ich richtig, wenn ich einen Service für die internen Sensordaten, einen für die GPS-Signale und einen zentralen Sammel-Service (Datenhandling) nutze und die Activity nur mit dem Sammel-Service kommuniziert?

Hilfreich?
Diskutiere mit!
Pascal P.
  • Mod
  • Blogger
  • Forum-Beiträge: 10.206

17.11.2018, 21:47:35 via Website

Kommt darauf an wie sehr du überhaupt multithreading brauchst.
Willst du dir Werte wrikllich alle sek oder alle x ms bekommen?
Ansonsten würde ein Service reichen, der alle in einer schleife abfragt.
Im Worst-Case ist dein letzter Wert so alt, wie deine schleife braucht.
Und das mit dem Ringspeicher geht nur, wenn deine UI sich nicht permanent aktualisieren soll, denn sonst musst du ständig aus dem Ringspeicher lesen (pollen) selbst wenn sich nix geändert hat.
Ich würde versuchen das Event gesteuert zu machen.

Das mit dem Service kannst du natürlich so machen, daber wenn du mehrere Services implementierst, dann musst du i.d.r. deutlich mehr zwischen Services kommunizieren und dann noch zur Activities.
Wie gesagt ich würde erstmal schauen, wie weit man mit nur einem Service kommt, der alles abfragt.
Die einzelnen Abfragen können ja vom Service selbst, schon in einem AsyncTask abgefragt werden, aber dafür brauchst du dann nicht mehrere Services.

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

17.11.2018, 22:07:09 via Website

Ja - ich würde die Sensorsignale alle 10ms oder 100ms auslesen, wobei z.B. der Beschleunigungssensor auch bis zu 2ms könnte, für mich aber nicht notwendig ist. GPS liegt systemseitig i.d.R. eher bei 1000ms.
In meinen ersten Testversuchen habe ich die UI in kürzester Zeit durch den Service über den Main-Thread lahm gelegt, auch wenn ich nur die reinen Float-Zahlen in der Activity habe anzeigen lassen.
Von daher würde ich gerne die Services von vornherein in min. einem eigenen Thread laufen lassen. Welche Thread-Variante eignet sich dafür?

Die strukturierte Kommunikation zwischen Services bzw. Activities ist ein weiterer Punkt den ich bei Gelegenheit gerne genauer verstehen möchte. Aus Matlab kenn ich nachvollziehbare Structures in denen alle Signalwerte logisch hinterlegt werden. Dort gibt es z.B. die Logik eines zentralen Streams der als Beispiel "Signal.Acceleration.X.value" lauten könnte. Ähnlich einer Baumstruktur werden alle Werte ebenweise in einem einzigen zentralen Objekt zusammengefasst. Gibt es so eine Logik auch in Android-Java oder fliegen hier alle Signalwerte alleine für sich durch den Programmraum und müssen individuell von anderen Services/Activities abgegriffen werden :-) ?

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

18.11.2018, 09:26:41 via Website

Ich habe gestern Abend noch etwas recheriert und das DevDeeds-Beispiel von oben ausprobiert. Dort wird die Position aber nicht über GPS abgefragt sondern über das Internet.

Heute früh habe ich dann folgende Vorgehensweise überlegt:
Im ersten Schritt erstelle ich einen einfachen Background-Thread der nur Text an die Activity gibt um zu testen ob das grundsätzlich funktioniert.
Xfabcirablog.weebly.com/blog/creating-a-never-ending-background-service-in-android

Dann würde ich im nächsten Schritt die GPS-Signale anhand dieses Beispiels in den Background-Service einbinden.
Xstackoverflow.com/questions/28535703/best-way-to-get-user-gps-location-in-background-in-android

Wenn das dann schon mal funktionieren sollte ist das nächste Ziel, die Signalkommunikation zwischen Service und Activity von Broadcast auf eine Zwischendatei (=Ringspeicher) umzustellen.

Klingt das zielführend?

Hilfreich?
Diskutiere mit!
Pascal P.
  • Mod
  • Blogger
  • Forum-Beiträge: 10.206

18.11.2018, 09:47:23 via Website

Dürfte schon passen, die Umsetung sollte funktionieren.
Leider bin ich in dem Thema nicht so drin, um dir da helfen zu können.

Aber wie gesagt: Einen Richspeicher pollen für Daten halte ich für wenig Sonvoll, daher eher eine Kombination aus Ringspeicher und Events

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

18.11.2018, 10:09:42 via Website

Hat ein dateibasierter Ringspeicher nicht den Vorteil, dass man die Synchronitätsprobleme verschiedener Threads umgeht indem der eine (Background-Thread) stumpf die Werte in die Datei schreibt und der UI-Thread unabhängig davon in einem frei definierbaren Zeitraster (z.B. alle 100ms) die Anzeige mit den Werten aus der Datei aktualisiert? Oder ist die UI damit zu sehr belastet?

Hilfreich?
Diskutiere mit!
Pascal P.
  • Mod
  • Blogger
  • Forum-Beiträge: 10.206

18.11.2018, 10:17:29 via Website

Gerade über eine Datei wirst du das Problem haben, der eine Thread hält die Datei offen und schreibt und dein lesethread kann die Datei nicht öffnen, da dein Schreib Thread dauernd die Datei offen hat.
Das würde ich mir nochmal überlegen, ich denke es funktioniert nicht.
Du kannst natürlich einen Ringspeicher auch per Code implementieren indem du irgendwo eine Singleton Klasse o.ä. hast du da Daten ablegst.
Und du kannst auch mit Events arbeiten, so dass du keine Synchronitätsprobleme bekommst.

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

18.11.2018, 11:36:48 via Website

Upps - guter Punkt mit dem gleichzeitigen Read/Write-Problem einer Datei... dann werde ich mir die Singleton-Klasse mal anschauen.

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

19.11.2018, 13:55:55 via Website

Ich muss nochmal auf das Threading-Thema zurückkommen... ich habe die letzten Stunden wieder versucht herauszufinden welches der sinnvollste Weg für meine 10ms-Sensorabfragen ist und bin vom AlarmManager über ServiceIntent und JobServiceIntent bis hin zum JobService über alle möglichen Varianten geleitet worden. Der JobService scheint vor allem ab Oreo bzgl. Background-Regulatorik und Akku-Verbrauch die nachhaltigste Variante zu sein, benötigt aber wieder eine separate Threadhandling-Methode um nicht im Main-Thread die Sensorabfrage durchzuführen.
Aktuell bin ich wieder vor lauter Bäumen im Wald so schlau (bzw. so doof) wie die letzten Jahre auch schon - warum ist das Threadhandling so unendlich (oder unnötig?) kompliziert?

Kann man auf einfache Weise (und Oreo+ stabil) selbst definieren welcher Service in welchem Thread laufen soll oder ist es evtl. sinnvoller aus einem Service im Main-Thread heraus alle 10ms einen AsyncTask aufzurufen?

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.920

19.11.2018, 14:02:07 via Website

Hallo ,

oder ist es evtl. sinnvoller aus einem Service im Main-Thread heraus alle 10ms einen AsyncTask aufzurufen?

Solche Zeiten kannst du erst mal komplett unter Android vergessen - das sind schliesslich 100Hz:-)
(Auch 100ms wären "sinnlos")

Das sind definitv Anwendungen für einen Embedded und nicht für eine VM.

Was mich eigentlich auf den Punkt bringt - Dein Vorhaben ist im Prinzip nicht für das von Dir verwendete System gedacht und deshalb verrennst du dich auch. Zumal ANdroid Listener basierend reagiert und auch so umgesetzt werden soll.

Egal wieviel du auch rumbastelst - das wird niemals unter Android stabil und ordentlich funktionieren.

— geändert am 19.11.2018, 14:16:31

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

19.11.2018, 14:19:06 via Website

Hallo Stefan,

danke für deine schnelle Antwort. Es gibt ja schon dutzende Apps die unter Android genau diese Sensor-Erfassung im 10-100Hz Bereich über mehrere Stunden ordentlich machen können (z.B. OBDLink, RaceChrono, Torque, DriveDeck, TrackAddict, usw.)

Irgendeinen Weg scheint es also zu geben um diese Signalerfassung zu ermöglichen. Die Frage ist nur welche hier am ehesten funktioniert.

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.920

19.11.2018, 14:35:46 via Website

Stopp, du wirfst da leider Äpfel und Birnen durcheinander. :-)

Vorweg : Ich entwickle schon einige Jahrzehnte in dem Bereich , deshalb kann ich ich dir auch sicher sagen , das das nicht funktionieren wird - leider.

Richtig , es gibt Apps, die eine Signalerfassung machen. Diese arbeiten allerdings mit einem Interrupt aus der Hardware heraus und weitaus langsamer, als deine 100Hz.
(ggf mit Datacollection).
Oder sie verwenden dazu externe Sensoren/Hardware (OBD - ELM 327 - Bluetooth/WIfi)
Ein Android System - direkt an den Bus angeschlossen - gibt es nicht - der Can-Bus ist zu schnell.

Es gibt also eine Hardware , die die Geschwindigkeit unterstützt , Daten filtert und sammelt und diese dann (meist MQTT) über Schnittstellen absetzt. Android reagiert darauf und schickt Dir einen Listener.
Bsp : Android unterstützt auch nicht die Geschwindigkeit einer USB 2.0 oder FTDI Schnittstelle. Bei voller Geschwindigkeit wird es bereits nach kurzer Dauer zu einem Überlauf, sprich verlorenen Daten am FIFO kommen.

Bedeutet - es wird nur dann geliefert, wenn die Hardware einen interrupt absetzt.
Also ist es ein passives system - denn Android reagiert auf die Veränderung.
Aktiv kannst du unter Android nicht pollen. (Oder man sollte es tunlichst in den Frequenzen vermeiden).

Grund : Android regelt seine Resourcen selbst und bestimmt von sich aus , was wichtig ist , und das ist erst mal die UI, dann mal lange gar nichts , und wenn es mal Zeit hat , dann eventuell.
(Überspitzt dargestellt)

Desweitern arbeiten Diese Threads nicht "stundenlang" das ist ein grosses Irrtum Deinerseits unter Android. (Siehe auch Themen wie Lifecycle und GC)
Anmerkung nebenbei : ein AsynTask ist auch nicht für den dauerhaften Einsatz gedacht .

Ergo : Du wirst unter Android niemals einen stabilen Poll - Thread in deiner Wunschrichtung zu Stande bekommen .
Android ist nur ein UI Interface , welches relativ dämlich auf Interrupts aus dem Kernel reagiert, aber niemals Aktiv arbeitet - und vor allem nie wird.

— geändert am 19.11.2018, 15:30:47

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

19.11.2018, 15:31:24 via Website

Sehr gut - jetzt bekommt die Diskussion endlich etwas Würze :-)

Ich glaube wir sind gar nicht so weit auseinander wie es erscheinen mag. Um CAN-Bus Anbindung in einem Fahrzeug geht es mir gar nicht. Mich interessieren zunächst nur die geräteinternen Sensoren und GPS. Eine Anbindung des OBD-Protokolls lassen wir erstmal aussen vor.
Ich habe ein OBD-ScanLink und mit der zugehörigen App OBDLink eine CSV-Datei meiner Autofahrt über eine Stunde mit dutzenden Signalen erzeugt. Größtenteils sind das OBD-Signale mit der bekannten ca. 1Hz-Abtastung, es werden aber auch die geräteinternen Sensoren aufgezeichnet und hier liegt die Abtastrate bei 20Hz (also 50ms).
Ich habe mal einen Screenshot einer Beispiel-Datei angefügt um es sich besser vorstellen zu können.
image

Mehr als das was dort gemacht wird suche ich eigentlich nicht für meine Test-App. 10ms ist nicht zwingend notwendig auch wenn dies die geräteinternen Beschleunigungssensoren grundsätzlich ermöglichen würden.

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.920

19.11.2018, 15:38:43 via Website

Apropos Würze, dann kommt jetzt mal Chili :-)

Mich interessieren zunächst nur die geräteinternen Sensoren und GPS.

(OBD bleibt jetzt erst mal weg)

Die internen Sensoren, wie auch das GPS sind Listener gesteuert.
Soweit dürfte uns das klar sein .

Jetzt habe ich aber immer noch nicht verstanden , wo dein "Problemchen" sitzt.
Schneller, als diese Listener liefern, wird du auf deinem Android-Device nicht bekommen ..

Aktiv pollen kannst du NICHT - Das Hardwareteil ( S6) entscheidet , was du darfst und kannst.

Oder reden wir jetzt gar aneinander vorbei ?? :-)

— geändert am 19.11.2018, 15:51:31

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 13

19.11.2018, 16:06:36 via Website

Passt alles - ich lasse die Habaneros im Schrank :D

Mehr als über die Listener möglich ist, ist nicht gefragt (mit 10-50ms eher sogar weniger).

Mein Problem liegt darin, dass ich verstehen muss wie die App-Architektur bzw. die Hintergrund-Services zu gestalten sind um diese über einen längeren Zeitraum laufenden Listener-Daten so zu sammeln, dass man im Nachgang mit diesen in einer Art Ringspeicher weiterarbeiten kann (z.B. Signalfilterung) und der UI-Thread nicht in die Knie geht (so wie in meiner aktuellen Erstumsetzung mit Services im Main-Thread nach wenigen Sekunden der Fall).

Wenn ich am Ende so eine CSV-Datei generiert bekommen würde wie es OBDLink schafft wäre ich schon einen Riesenschritt weitergekommen. Und in diesem Zuge müsste ich verstehen wie man in Android möglichst strukturiert mit interner Signalverarbeitung umgehen kann, also wie der "Datenstream" aussehen sollte um möglichst einfach aus verschiedenen Methoden auf diese Einzelwerte zugreifen zu können.

— geändert am 19.11.2018, 16:10:14

Hilfreich?
Diskutiere mit!
  • Forum-Beiträge: 2.920

19.11.2018, 16:14:23 via Website

Arrgh ... schwere Geburt - Ich fang mal an :-)

a) Es gibt keinen "dauer" Service - das ist schon mal fakt. EIn Service kann jederzeit von ANdroid abgeschossen werden , wenn es dazu in Laune ist.
Es gibt einen Workaround mit Timern , die Dir nach einen gewissen Zeit den Service neu starten (HeartBeat). In der Stop/Start-Phase ( min 5000 ms ) gibt es halt keine Daten.

b) Um deinen Ringpuffer zu bauen (FIFO) wird ich im Service eine SQLite DB implementieren.
in deinem Main UI Thread würde ich dann nach belieben diesen auslesen , anzeigen (mit Delay) und den Datensatz erst mal mit einem "verarbeitet" Flag markieren.
Nicht mehr benötigte Datensätze, dann irgendwann in einer Pause bereinigen / löschen.

Die SqLite ist schon verdammt schnell.

Anmerkung : Die Sensoren ( nicht GPS) würde ich auch vorher mit einem Filter belegen - denn sonst kommt viel (nicht brauchbarer) Schrott.
https://github.com/KalebKE/FSensor

— geändert am 19.11.2018, 16:24:56

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Diskutiere mit!

Empfohlene Artikel