Einführung in Android-Architekturkomponenten
German (Deutsch) translation by Valentina (you can also view the original English article)
Android wurde bereits 2005 auf der Welt eingeführt. In diesen zwölf Jahren hat die Plattform erstaunliche Erfolge verzeichnet und ist das am häufigsten installierte mobile Betriebssystem. In dieser Zeit wurden 14 verschiedene Versionen des Betriebssystems eingeführt, wobei Android immer ausgereifter wurde. Ein sehr wichtiger Bereich der Plattform wurde jedoch weiterhin ignoriert: ein Standardarchitekturmuster, das die Plattformmerkmale beherrscht und einfach genug ist, um vom durchschnittlichen Entwickler verstanden und übernommen zu werden.
Nun, besser spät als nie. Bei der letzten Google I/O entschied sich das Android-Team schließlich dafür, dieses Problem anzugehen und auf das Feedback von Entwicklern auf der ganzen Welt zu reagieren, eine offizielle Empfehlung für eine Android-Anwendungsarchitektur zu verkünden und die Bausteine für die Implementierung bereitzustellen: die neue Architektur Komponenten. Und noch besser, sie haben es geschafft, ohne die Offenheit des Systems, das wir alle kennen und lieben, zu beeinträchtigen.



ArchitekturkomponentenIn diesem Lernprogramm werden wir die standardisierte Architektur erkunden, die das Android-Team bei Google I/O vorgeschlagen hat, und die wichtigsten Elemente der neuen Architekturkomponenten betrachten: Lifecycle
, ViewModel
, LifeData
und Room
. Wir werden dem Code nicht zu viel Aufmerksamkeit schenken, sondern uns auf das Konzept und die Logik hinter diesen Themen konzentrieren. Wir werden auch ein paar einfache Ausschnitte betrachten, die alle mit Kotlin geschrieben wurden, einer erstaunlichen Sprache, die jetzt offiziell von Android unterstützt wird.
1. Was fehlte Android?
Wenn Sie Ihre Reise als Entwickler gerade erst beginnen, wissen Sie möglicherweise nicht genau, worüber ich spreche. Schließlich kann die Anwendungsarchitektur auf den ersten Blick ein obskures Thema sein. Aber glauben Sie mir, Sie werden bald lernen, wie wichtig es ist! Wenn eine Anwendung wächst und komplexer wird, wird ihre Architektur immer wichtiger. Es kann Ihre Arbeit buchstäblich zur Seligkeit oder zur Hölle machen.
Anwendungsarchitektur
Grob gesagt: Eine Anwendungsarchitektur ist ein konsistenter Plan, der vor Beginn des Entwicklungsprozesses erstellt werden muss. Dieser Plan enthält eine Karte, wie die verschiedenen Anwendungskomponenten organisiert und miteinander verbunden werden sollen. Es enthält Richtlinien, die während des Entwicklungsprozesses befolgt werden sollten, und zwingt einige Opfer (im Allgemeinen mehr Klassen und Boilerplate-Elemente), die Ihnen letztendlich beim Erstellen einer gut geschriebenen Anwendung helfen, die besser testbar, erweiterbar und wartbar ist.
Software-Anwendungsarchitektur ist der Prozess der Definition einer strukturierten Lösung, die alle technischen und betrieblichen Anforderungen erfüllt und gleichzeitig die üblichen Qualitätsmerkmale wie Leistung, Sicherheit und Verwaltbarkeit optimiert. Es umfasst eine Reihe von Entscheidungen, die auf einer Vielzahl von Faktoren basieren, und jede dieser Entscheidungen kann erhebliche Auswirkungen auf die Qualität, Leistung, Wartbarkeit und den Gesamterfolg der Anwendung haben.
- Microsofts Software-Architektur- und Designhandbuch
Eine gute Architektur berücksichtigt viele Faktoren, insbesondere die Systemeigenschaften und -grenzen. Es gibt viele verschiedene architektonische Lösungen, alle mit Vor- und Nachteilen. Einige Schlüsselbegriffe sind jedoch allen Visionen gemeinsam.
Alte Fehler
Bis zur letzten Google I/O hat das Android-System keine spezifische Architektur für die Anwendungsentwicklung empfohlen. Das bedeutet, dass Sie völlig frei waren, jedes Modell auf den Markt zu bringen: MVP, MVC, MVPP oder sogar überhaupt kein Muster. Darüber hinaus bot das Android-Framework nicht einmal native Lösungen für Probleme, die vom System selbst erstellt wurden, insbesondere für den Lebenszyklus der Komponente.



Wenn Sie also ein Model View Presenter-Muster in Ihre Anwendung übernehmen wollten, mussten Sie Ihre eigene Lösung von Grund auf entwickeln, viel Boilerplate-Code schreiben oder eine Bibliothek ohne offizielle Unterstützung verwenden. Durch das Fehlen von Standards wurden viele schlecht geschriebene Anwendungen erstellt, deren Codebasis schwer zu warten und zu testen war.
Wie gesagt, diese Situation wird seit Jahren kritisiert. In der Tat habe ich vor kurzem über dieses Problem und seine Lösung in meiner Anleitung Wie kann man Model View Presenter auf Android übernehmen berichtet. Aber das Wichtigste ist, dass das Android-Team nach 12 langen Jahren endlich beschlossen hat, sich unsere Beschwerden anzuhören und uns bei diesem Problem zu helfen.
2. Android-Architektur
Der neue Android-Architekturhandbuch definiert einige Schlüsselprinzipien, denen eine gute Android-Anwendung entsprechen sollte, und schlägt dem Entwickler einen sicheren Pfad zum Erstellen einer guten App vor. Der Leitfaden besagt jedoch ausdrücklich, dass die dargestellte Route nicht obligatorisch ist und letztendlich die Entscheidung persönlich ist. Es ist der Entwickler, der entscheiden sollte, welche Art von Architektur verwendet werden soll.
Laut Anleitung sollte eine gute Android-Anwendung eine solide Trennung der Bedenken bieten und die Benutzeroberfläche von einem Modell ablenken. Code, der keine Interaktion mit einer Benutzeroberfläche oder einem Betriebssystem ausführt, sollte nicht in einer Aktivität oder einem Fragment enthalten sein, da durch das Reinigen der Codes viele Probleme im Zusammenhang mit dem Lebenszyklus vermieden werden. Schließlich kann das System Aktivitäten oder Fragmente jederzeit zerstören. Außerdem sollten die Daten mit Modellen behandelt werden, die von der Benutzeroberfläche isoliert sind, und folglich aus Lebenszyklusproblemen.
Die neue empfohlene Architektur
Die von Android empfohlene Architektur kann nicht einfach unter den bekannten Standardmustern gekennzeichnet werden. Es sieht aus wie ein Model View Controller-Muster, ist jedoch so eng mit der Architektur des Systems verbunden, dass es schwierig ist, jedes Element mit den bekannten Konventionen zu kennzeichnen. Das ist jedoch nicht relevant, da das Wichtigste ist, dass die neuen Architekturkomponenten für die Trennung der Anliegen mit hervorragender Testbarkeit und Wartbarkeit erforderlich sind. Und noch besser ist es einfach zu implementieren.
Um zu verstehen, was das Android-Team vorschlägt, müssen wir alle Elemente der Architekturkomponenten kennen, da diese die Hauptanforderung für uns sind. Es gibt vier Komponenten mit jeweils einer bestimmten Rolle: Room
, ViewModel
, LiveData
und Lifecycle
. All diese Teile haben ihre eigenen Verantwortlichkeiten und sie arbeiten zusammen, um eine solide Architektur zu schaffen. Schauen wir uns ein vereinfachtes Diagramm der vorgeschlagenen Architektur an, um sie besser zu verstehen.



Wie Sie sehen, haben wir drei Hauptelemente, von denen jedes seine Verantwortung trägt.
- Die
Aktivity
und dasFragment
stellen dieView
-Ebene dar, die sich nicht mit Geschäftslogik und komplexen Vorgängen befasst. Es konfiguriert nur die Ansicht, handhabt die Benutzerinteraktion und überwacht und zeigtLiveData
-Elemente, die aus demViewModel
entnommen wurden. - Das
ViewModel
überwacht automatisch denLifecycle
-Status der Ansicht und behält die Konsistenz bei Konfigurationsänderungen und anderen Android-Lebenszyklusereignissen bei. Es wird auch von der Ansicht verlangt, Daten aus demRepository
abzurufen, das als beobachtbareLiveData
bereitgestellt wird. Es ist wichtig zu verstehen, dass dasViewModel
nie direkt auf dieView
verweist und dass die Aktualisierungen der Daten immer von derLiveData
-Entität vorgenommen werden. - Das
Repository
ist keine spezielle Android-Komponente. Es ist eine einfache Klasse ohne besondere Implementierung, die dafür verantwortlich ist, Daten aus allen verfügbaren Quellen von einer Datenbank zu Web-Services abzurufen. Es verarbeitet all diese Daten, wandelt sie im Allgemeinen in beobachtbareLiveData
-Daten um und macht sie fürViewModel
verfügbar. - Die
Room
-Datenbank ist eine SQLite-Zuordnungsbibliothek, die den Umgang mit einer Datenbank erleichtert. Es schreibt automatisch eine Tonne Boilerplate, prüft Fehler zur Kompilierzeit und kann am besten Abfragen direkt mit beobachtbarenLiveData
zurückgeben.
Ich bin mir sicher, dass Sie bemerkt haben, dass wir viel über Observables gesprochen haben. Das Observer-Muster ist eine der Grundlagen der LiveData
-Elemente und der Lifecycle
-fähigen Komponenten. Dieses Muster ermöglicht es einem Objekt, eine Liste von Beobachtern über Änderungen des Zustands oder der Daten zu benachrichtigen. Wenn also eine Aktivität eine LiveData
-Entität beobachtet, erhält sie Aktualisierungen, wenn diese Daten irgendeine Art von Änderung erfahren.
Eine weitere Android-Empfehlung ist die Konsolidierung seiner Architektur mit einem Dependency Injection-System, wie Dagger 2 von Google oder mit dem Service Locator-Muster (das viel einfacher ist als DI, jedoch ohne viele Vorteile). DI oder Service Locator werden in diesem Tutorial nicht behandelt, aber Envato Tuts+ bietet einige hervorragende Tutorials zu diesen Themen. Beachten Sie jedoch, dass es einige Besonderheiten bei der Arbeit mit Dagger 2 und Android-Komponenten gibt, die im zweiten Teil dieser Serie erläutert werden.
3. Architekturkomponenten
Wir müssen tief in die Aspekte der neuen Komponenten eintauchen, um dieses Architekturmodell wirklich verstehen und anwenden zu können. In diesem Tutorial werden wir jedoch nicht auf alle Details eingehen. Aufgrund der Komplexität der einzelnen Elemente werden in diesem Lernprogramm nur die Grundgedanken hinter jedem Element beschrieben und einige vereinfachte Codeausschnitte dargestellt. Wir werden versuchen, ausreichend Platz für die Präsentation der Komponenten zu schaffen und Ihnen den Einstieg zu erleichtern. Aber keine Angst, denn zukünftige Artikel in dieser Reihe werden tief graben und alle Besonderheiten der Architekturkomponenten abdecken.
Lifecycle-Aware-Komponenten
Mit den meisten Android-App-Komponenten sind Lebenszyklen verknüpft, die direkt vom System selbst verwaltet werden. Bis vor kurzem war es Aufgabe des Entwicklers, den Status der Komponenten zu überwachen und entsprechend zu handeln, um Aufgaben rechtzeitig zu initialisieren und zu beenden. Es war jedoch sehr einfach, sich zu verwirren und Fehler in Bezug auf diese Art von Operation zu machen. Aber das android.arch.lifecycle
-Paket hat das alles geändert.
Aktivitäten und Fragmenten ist jetzt ein Lifecycle
-Objekt zugeordnet, das von LifecycleObserver
-Klassen wie einem ViewModel
oder jedem Objekt, das diese Schnittstelle implementiert, beobachtet werden kann. Das bedeutet, dass der Beobachter Aktualisierungen über die Zustandsänderungen des beobachteten Objekts erhält, z. B. wenn eine Aktivität angehalten wird oder wann sie gestartet wird. Es kann auch den aktuellen Status des beobachteten Objekts überprüfen. Daher ist es jetzt viel einfacher, Operationen auszuführen, bei denen der Lebenszyklus des Frameworks berücksichtigt werden muss.



Um eine Aktivity
oder ein Fragment
zu erstellen, die diesem neuen Standard entsprechen, müssen Sie zunächst eine LifecycleActivity
oder ein LifecycleFragment
erweitern. Es ist jedoch möglich, dass dies nicht immer notwendig ist, da das Android-Team diese neuen Tools vollständig in sein Framework integrieren möchte.
class MainActivity : LifecycleActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
Der LifecycleObserver
empfängt Lifecycle
-Ereignisse und kann durch Annotation reagieren. Es ist keine Methodenüberschreibung erforderlich.
class MainActivityObserver : LifecycleObserver, AnkoLogger { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { info("onResume") } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { info("onPause") } }
Die LiveData
-Komponente
Die LiveData
-Komponente ist ein Datenhalter, der einen beobachtbaren Wert enthält. Da der Beobachter während der LiveData
-Instantiierung einen Lifecycle
bereitgestellt hat, verhält sich LiveData
entsprechend dem Lifecycle
-Status. Wenn der Lifecycle
-Status des Beobachters STARTED
oder RESUMED
ist, ist der Observer active
. Andernfalls ist es inactive
.
LiveData
weiß, wann die Daten geändert wurden und ob der Beobachter active
ist und ein Update erhalten soll. Ein weiteres interessantes Merkmal von LiveData
ist die Fähigkeit, den Beobachter zu entfernen, wenn er sich im Status Lifecycle.State.DESTROYED
befindet, wodurch Speicherverluste bei der Beobachtung durch Aktivitäten und Fragmente vermieden werden.



Ein LiveData
muss die Methoden onActive
und onInactive
implementieren.
class LocationLiveData(context: Context) : LiveData<Location>(), AnkoLogger, LocationListener { private val locationManager: LocationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager override fun onActive() { info("onActive") locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, this) } override fun onInactive() { info("onInactive") locationManager.removeUpdates(this) } // .... }
Um eine LiveData
-Komponente beobachten zu können, müssen Sie den observer(LifecycleOwner, Observer<T>)
aufrufen.
class MainActivity : LifecycleActivity(), AnkoLogger { fun observeLocation() { val location = LocationLiveData(this) location.observe(this, Observer { location -> info("location: $location") }) } }
Die ViewModel
-Komponente
Eine der wichtigsten Klassen der neuen Architekturkomponenten ist ViewModel
, das Daten enthält, die sich auf die Benutzeroberfläche beziehen, und deren Integrität bei Konfigurationsänderungen wie Bildschirmdrehungen beibehalten wird. Das ViewModel
kann mit dem Repository
sprechen, LiveData
daraus abrufen und verfügbar machen, damit es von der Ansicht beobachtet werden kann. ViewModel
muss auch nach Konfigurationsänderungen keine neuen Aufrufe an das Repository
vornehmen, wodurch der Code stark optimiert wird.



Um ein Ansichtsmodell zu erstellen, erweitern Sie die ViewModel
-Klasse.
class MainActivityViewModel : ViewModel() { private var notes: MutableLiveData<List<String>>? = null fun getNotes(): LiveData<List<String>> { if (notes == null) { notes = MutableLiveData<List<String>>() loadNotes() } return notes!! } private fun loadNotes() { // do async operation to fetch notes } }
Um auf eine Ansicht zuzugreifen, können Sie ViewProviders.of(Activity|Fragment).get (ViewModel::class)
aufrufen. Diese Factory-Methode gibt eine neue Instanz von ViewModel
zurück oder ruft gegebenenfalls die beibehaltene Instanz auf.
class MainActivity : LifecycleActivity(), AnkoLogger { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel = ViewModelProviders.of(this) .get(MainActivityViewModel::class.java) viewModel.getNotes().observe( this, Observer { notes -> info("notes: $notes") } ) } }
Die Room
-Komponente
Android unterstützte SQLite von Anfang an; Damit dies funktioniert, war es jedoch immer notwendig, viel Boilerplate zu schreiben. Außerdem hat SQLite keine POJOs (einfache Java-Objekte) gespeichert und keine Abfragen zur Kompilierzeit geprüft. Room
kommt, um diese Probleme zu lösen! Es ist eine SQLite-Zuordnungsbibliothek, die Java-POJOs beibehalten kann, Abfragen direkt in Objekte konvertiert, Fehler zur Kompilierzeit prüft und LiveData
-Observables aus Abfrageergebnissen erzeugt. Room
ist eine Object Relational Mapping-Bibliothek mit einigen coolen Android-Extras.
Bislang konnten Sie die meisten Room
von ORM Android-Bibliotheken nutzen. Keiner von ihnen wird jedoch offiziell unterstützt, und vor allem können sie keine LifeData
-Ergebnisse erzielen. Die Room
-Bibliothek passt perfekt als persistente Schicht in die vorgeschlagene Android-Architektur.
Zum Erstellen einer Room
-Datenbank benötigen Sie ein @Entity
-Element, das beliebig sein kann. Das kann ein beliebiges Java-POJO sein, eine @Dao
-Schnittstelle für Abfragen und Eingabe-/Ausgabeoperationen sowie eine abstrakte @Database
-Klasse, die RoomDatabase
erweitern muss.
@Entity class Note { @PrimaryKey var id: Long? = null var text: String? = null var date: Long? = null }
@Dao interface NoteDAO { @Insert( onConflict = OnConflictStrategy.REPLACE ) fun insertNote(note: Note): Long @Update( onConflict = OnConflictStrategy.REPLACE ) fun updateNote(note: Note): Int @Delete fun deleteNote(note: Note): Int @Query("SELECT * FROM note") fun findAllNotes(): LiveData<Note> // on Kotlin the query arguments are renamed // to arg[N], being N the argument number. // on Java the arguments assume its original name @Query("SELECT * FROM note WHERE id = :arg0") fun findNoteById(id: Long): LiveData<Note> }
@Database( entities = arrayOf(Note::class), version = 1) abstract class Databse : RoomDatabase() { abstract fun noteDAO(): NoteDAO }
Hinzufügen von Architekturkomponenten zu Ihrem Projekt
Um die neuen Architekturkomponenten zu verwenden, müssen Sie zunächst das Google-Repository Ihrer build.gradle
-Datei hinzufügen. Weitere Informationen finden Sie im offiziellen Leitfaden.
allprojects { repositories { jcenter() // Add Google repository maven { url 'https://maven.google.com' } } }
Schlussfolgerung
Wie Sie sehen, umfasst die von Android vorgeschlagene standardisierte Architektur viele Konzepte. Erwarten Sie noch kein umfassendes Verständnis dieses Themas. Schließlich stellen wir das Thema lediglich vor. Sie verfügen jedoch bereits über genügend Kenntnisse, um die Logik der Architektur und die Rollen der verschiedenen Architekturkomponenten zu verstehen.
Wir sprachen über die meisten Themen, die sich auf die vorgeschlagene Android-Architektur und ihre Komponenten beziehen. Details zur Komponentenimplementierung und einige Extras wie die Repository
-Klasse und das Dagger 2-System können in diesem ersten Teil jedoch nicht behandelt werden. Wir werden diese Themen in den nächsten Beiträgen untersuchen.
Bis bald!