MVC Pattern und ListView Adapter.

S

Samsung I7500

Erfahrenes Mitglied
5
Hallo!

Ich baue gerade eine App, bei der ich auch versuche das MVC Pattern umzusetzen. Die App arbeitet dabei mit zwei!!!!! Models, die im Betrieb auf Benutzerwunsch getauscht werden können. Die Architektor sieht derzeit wie folgt aus:

1 MainActivity, die in dem Fall eine FragmentActivity ist.
x Fragments, die je nach Navigation angezeigt werden.
1 Service, der sowohl von der MainActivity als auch von den einzelnen Fragments gebunden wird.
2 Models, die im Service gespeichert sind.

Die Kommunikation zwischen den Fragments und den Models erfolgt über den Service, somit dient der Service quasi als Controller.

Nun beinhalten einige Fragments ListViews. Entsprechend MVC besteht nun der Wunsch, die Models mit Adaptern für die ListViews auszustatten, und je nachdem welches Model gerade aktiv ist, die entsprechenden Adapter an die ListViews der Fragments zu koppeln. Damit würden die Adapter innerhalb der Models manipuliert mit Daten gefüttert werden, und beim Switch würden in den ListViews entsprechend die Adapter ausgetauscht werden.

Das ganze scheitert jedoch an der altbekannten Fehlermeldung:
Code:
only the original thread that created a view hierarchy can touch its views
Gibt es irgendeine Möglichkeit das ganze irgendwie zu realisieren?

Vielen Dank schonmal. :)
 
Hallo,

ganz kurz:

Eine Art von MVC gibt es schon bei Android. Dabei sind die Activities und Fragmente die Controller. Und das XML - Layout das View. Mit den üblichen Einschränkungen kann man die Optik fast komplett in das XML Layout auslagern (Du kannst dann alles mögliche ändern, ohne Fragment/Activity anzufassen.)

Ich würde nicht unbedingt ein eigenes MVC - Pattern implementieren. Das dürfte nur zu Erhöhung der Komplexität beitragen.

Für deinen Fehler schau mal dort nach:
java - Android "Only the original thread that created a view hierarchy can touch its views." error in Fragment - Stack Overflow

multithreading - Android "Only the original thread that created a view hierarchy can touch its views." - Stack Overflow
 
Hallo,

ich bin irgendwie nicht so der Fan von dem XML Gedöns. Und die Architectur wie ich sie habe hat schon so ihre Gründe. Die App ist ein Audioplayer, der nebenher auch als Remotecontrol für einen Audioserver dient. Da die Funktionalitäten auf beiden Seiten identisch ist, hat die App 2 Models. Das eine, das die Musik auf dem Smartphone verwaltet und den lokalen Player steuert und das andere, das selbiges mit dem Server macht.

Da der lokale Player zu stottern anfängt sobald die App in den Hintergrund geht läuft entsprechend der Service im Hintergrund, der auch beim Titelupdate Notifications postet oder auch die Hardware-Buttons abhört. Daher befinden sich die Models auch dort.

Was die Fehlermeldung angeht, die Lösung mit dem runOnUiThread ist mir durchaus bekannt. Gerade die macht das ganze ja kompliziert weil ich aus den Models eben nicht einfach so auf den Adapter zugreifen kann.

Allerdings kommt mir gerade eine neue Idee wie ich die Problematig lös. Mal gucken...
 
only the original thread that created a view hierarchy can touch its views
Deine Implementierung des MVC - Pattern ist nur indirekt für diesen Fehler verantwortlich. Der Fehler tritt auf, wenn man von einem Thread auf den MainThread zu greifen will. Spricht, die Komminikation zwischen den beiden Thread wurde falsch implementiert.

Hast du ein Handler oder Intents und BroadcastReceiver benutzt.

Um das XML Gedöns wirst du wohl nicht herum kommen. Die Idee für den Einsatz in Android, ist die Verwirklichung eines MVC. Durch das XML kannst du das Aussehen und die Struktur eines Layout von der Implementierung der Logik in der Activity / Fragment trennen.

Android ist für den Einsatz von XML optimiert worden. Um ein eigenes MCV zu implementieren, muss du zum Teil die Strukturen von Android aushebeln, was nicht gerade performant ist.

Controller (MVC): Hast du dir schon mal überlegt, warum Activity und Service von der Klasse Context (eine Schnittstelle) erben. Und welche Klasse wohl die Medoden onPause(), onResume(), onCreate usw. aufruft.
 
Die Bedeutung dieser Fehlermeldung ist mir schon klar was es damit auf sich hat. Darum interessiert es mich ja gerade ob und wie ich den Adapter in einen anderen Thread verbannen kann. Da dies wohl aber nicht möglich zu sein scheint muss ich Teile des Models wohl oder übel in die Adapter verlagern und dann eben doch mit runOnUiThread befüllen. Dürfte mir auf ne andere Art und Weise aber irgendwie entgegenkommen...

Zum Thema XML: Find ich von der Idee her ja nice, mag die Umsetzung aber nicht. Mit entsprechendem Data-Binding ala AngularJS oder Microsoft WPF würde das ganze anders aussehen. ;)
 
Den Adapter kannst du nicht auslagern, den er füllt das Model in das View. Und dafür braucht er auf beide eine Referenz.

Einen reinen MVC kannst du unter Android nicht umsetzen.

Der Eintrittspunkt in eine App ist nicht die MainMethode sondern eine Activity. Und diese hat Eigenschaften eines Views, und sie kann zusätzlich als Controller agieren. Und damit hängt jeder selbst implementierter Controller an einem View. ;)

Und du hast eine Komponente, die nicht im MVC enthalten sind - BroadcastReceiver und Intents (insbesondere PendingIntents).

Aber das einzige Argument, das gegen deinen Ansatz spricht, hat nichts mit deiner Abneigung gegen XML zu tun. Alles, was die Komplexität eines Projekt erhöht, sollte man einfach sein lassen, wenn man mit mehreren Leuten zusammen programmiert.
Nicht jeder wird z. B. Lust und Zeit (Abgabetermin ;) ) haben, die Beispiele von Google an deine Struktur anzupassen.

OOP ist schön, aber nicht immer sinnvoll.
 
Wie kommst Du darauf dass ich mit mehreren Programmier? Ich baue die App alleine! ;)

Würde ich mit anderen Programmieren gäbe es da entsprechend Absprachen, und je nachdem ob ich nun Projektleiter wär würde ich entweder die Struktur vorgeben, oder mich aber der vorgegebenen fügen. Da dies aber nicht der Fall ist... ;)

Aber wie schon gesagt, für das Adapterproblem habe ich bereits eine Lösung. ;)
 
:)

Bin recht neugierig, wie dein Code im Endeffekt aussieht. Insbesondere wie du mit der Neigung von Android, ausgiebig von RPC und Mapping Gebrauch zu machen, umgehst. ;)

Application Fundamentals | Android Developers

Ich finde dein Ansatz gut, bin aber immer die Meinung, dass die einfachste Lösung die beste ist. Und das jeder Overhead meist mehr Probleme macht, als er löst.

Ich denke, das Projekt könnte dich vielleicht interessieren (MVVC):
http://robobinding.github.io/RoboBinding/
 
Im Grunde ist mein Code wie folgt aufgebaut:

Ich habe eine MainActivity, die eine FragmentActivity ist. Daneben habe ich einzelne Fragmente, die halt meine Views sind. Jede wird Programmtechnisch aufgebaut, also ohne den XML Krams.

Sowohl die Activity als auch die View binden jetzt den Service. Dieser wiederum instanziiert beide Models, und setzt eines davon aktiv.

Jede View implementiert nun ein eigenes Interface, und registriert dieses beim Service. Der Service leitet dieses an die Models weiter. Damit können die Models Daten und Commands an die einzelnen Views senden, wobei halt nur die des aktiven Models an die View weitergeleitet werden (im Moment können noch beide gleichzeitig senden, der Lock dass das Inaktive nicht sendet kommt aber noch rein). In den Views folgt dann das übliche runOnUiThread.

Vorher habe ich den Fehler gemacht (und nein, ich habe keine Ahnung was mich dazu geritten hat), ein globales Interface für alle Views zu erstellen. Damit begann auch die Unübersichtlichkeit im Code. :laugh:

Jetzt habe ich diese in kleine, für jede View eigenständige Interfaces aufgeteilt, so dass jede View nur die Daten bekommt die sie auch wirklich benötigt.

Damit habe ich im Grunde die von mir gewünschte Architektur, die ich auch gut im Überblick hab.

Was XML und Overhead angeht: Die XML Funktionalität ist ja (soweit ich das beurteilen kann) nur dafür da, um die View zu erstellen, sprich die entsprechenden Klassen zu instanziieren und miteinander zu verknüpfen. Im Code muss man jede View dann wieder via findViewById suchen, was folglich zunächst einmal eine Rekursion mit sich bringt (außer Android greift hier in die Trickkiste und speichert die Elemente in einer Liste so dass es sie nur noch raussuchen muss).

Je nachdem wo man das ganze anwendet (angenommen das kommt in einem Clicklistener eines Buttons vor), wird die Rekursion immer und immer wieder aufgerufen, womit es sich anbieten würde die View einmal zu suchen und irgendwo im Code zu speichern, damit man sie nicht jedes mal neu suchen muss. Und wenn man dann schon speichert, dann kann man die View gleich programmiertechnisch erstellen (was ich dahingehend auch bevorzuge).

Damit bietet das XML von Android dahingehend keine Vorteile, außer dass ich neben Java noch mit XML rumhantieren und damit was die Entwicklung angeht auch wieder Umdenken muss. Würde es Bindings unterstützen würde das ganze wieder ganz anders aussehen.

Da scheint RoboBinding recht interessant zu sein. Werd ich mir mal angucken.

Und klar, man kann es wie Du es beschreibst für ein MVC Pattern mißbrauchen, wenn die Activities/Fragmente dann die Controller sein sollen. Allerdings ist das nicht wirklich meine Vorstellung von einem MVC Pattern, da ich die Activities hier sogar als Code-Behind Dateien ähnlich Microsoft WPF ansehen würde. Damit wäre das ganze für mich wieder kein MVC mehr. :winki:

Ach ja, das mit den Receivern werd ich mir auch nochmal genauer angucken. Glaube spätestens beim Thema Widget werd ich wohl darauf zugreifen müssen. Im moment bleibt das ganze aber so wie es ist. ;)
 
Zuletzt bearbeitet:
Hört sich interessant an.

(außer Android greift hier in die Trickkiste und speichert die Elemente in einer Liste so dass es sie nur noch raussuchen muss)

Richtig geraten. ;)

Warum brauchst du ein aktives und ein passives Model. Wieso unterbindest du ein einem Fall die direkte Notifikation vom Model zum View? (Wobei ich das passive Model vorziehe ;) ).

Wie hast du das Problem mit den verschiedenen Auflösungen und Displaygrößen gelöst? Gerade hier liegt die Stärke von XML.

Die Receiver solltest du Dir unbedingt anschauen. Sie sind ein zentrales Element der Android Architektur. Sie sind einer der 4 App Componenten. Und über alle Komponenten kann das System auf die Applikation zugreifen (Lifecycle). (Auch hier ist XML im Spiel - Android Manifest).
Und man kann mit ihnen auf Ereignisse im System reagieren.
 
Ich habe ein Lokal-und Remote-Model. Beide verwalten jeweils eine Musiklibrary, wobei das Lokal-Model die Library des Smartphones und das Remote-Model die Library des Servers verwaltet. Zur Verwaltung gehören aufgaben wie Sortierung der Musik nach Labels, Künstlern, Playlisten, etc..

Die Verwaltung der Serverlibrary ist dabei bewusst auf den Client ausgelagert. Der Server hat nur minimale Informationen (Ort der geladenen Audiodateien), das Kommando welcher Song abgespielt werden soll muss dabei vom Client kommen. Das hat den ganz einfachen Grund dass der Server so minimal gehalten bleibt und damit auch auch schwacher Hardware laufen kann, in meinem Fall auf einem Raspberry PI.

Da er zusätzlich aber auch noch Aufgaben wie Encoding oder Streaming zu erfüllen hat ist alles was Resourcen schont herzlich wilkommen. Daher übernehmen die Clients eben die Verwaltung.

Da beim Start das lokale Model aktiv ist, und der Benutzer sich aus der App mit dem Server verbinden kann, kommt als zweites Model das Remote-Model dazu, welches beim dis/connect mit dem lokalen Model ausgetauscht wird. Das lokale Model bleibt dabei erhalten damit beim Disconnect die Musik nicht erneut gescannt werden muss.

Und dass ich die Notifications an die View unterbinde ist dass halt nicht beide Models gleichzeitig posten sollen. Die App kriegt von beiden Models z.B. ein State-Update aus dem hervor geht welche Buttons aktiv sind und welche nicht (Bsp. Player spielt nicht, also sind Stop und Pause deaktiviert. Aktueller Playlisteintrag ist 1, Repeat oder Shuffle sind off, also ist der Prev-Button deaktiviert). Die sollen sich halt nicht gegenseitig in den Views herumpfuschen. ;)

Das Problem der verschidenen Auflösungen ist derzeit ungelöst, da die App auf meinem Tablet den Dienst verweigert (findet die Audio Library nicht, obwohl diese vom Compiler mit installiert wird). Gedacht ist aber eine Lösung ala Twitter Bootstrap, sprich das ganze soll responsiv gelöst werden. Die Möglichkeiten dazu sind ja da, muss ich halt nur noch realisieren. ;)

Die Notifications (Stichwort: Foreground-Service) werden beim lokalen Model angezeigt wenn der Player aktiv spielt oder pausiert ist, beim Remote-Model durchgehend solange der Benutzer verbunden ist. :)
 

Ähnliche Themen

A
Antworten
10
Aufrufe
1.021
swa00
swa00
M
  • MikelKatzengreis
Antworten
5
Aufrufe
128
swa00
swa00
D
Antworten
9
Aufrufe
1.767
jogimuc
J
Zurück
Oben Unten