Vor kurzem haben wir die Pyrus-App für Android komplett überarbeitet. Die erste Version der Anwendung funktionierte bereits unter Android 2.2. Da wir uns weigerten, Android unter 4.1 zu unterstützen, konnten wir die angesammelten technischen Schulden zurückzahlen und den Quellcode erheblich vereinfachen. Ja, wir haben einen Teil der Benutzer verloren (weniger als 1%), aber andererseits haben wir den Entwicklern die Zeit gespart, seltene Fehler zu beheben. Wir werden es in die Entwicklung von Funktionen für alle aktuellen und neuen Benutzer investieren können. Auf lange Sicht ist dies viel wichtiger.
Hier teilen wir Erfahrungen mit, die für diejenigen nützlich sein können, die erwägen, mit der Entwicklung für die Android-Plattform zu beginnen.
Android zieht gute Entwicklungstools an, eine bewährte Sprache (Java) mit der üblichen Syntax, ein großes Publikum von Benutzern. Das Erstellen praktischer Anwendungen auf Android ist jedoch schwierig: Trotz der entwickelten API kommt es häufig vor, dass keine fertige Komponente mit geeignetem Verhalten vorhanden ist. Sie müssen entweder die Ideen des UX-Designers aufgeben oder sich darauf verlassen, von Bibliotheken von Drittanbietern abhängig zu sein, oder sich selbst schreiben. Nicht alle Architekturlösungen in Android haben sich als erfolgreich erwiesen, und dies erhöht auch die Codemenge ohne offensichtlichen Nutzen.
Sterben und Aufstehen aus der Asche
Die Benutzeroberfläche in Android basiert auf Aktivität. Dies ist ein Container, der normalerweise den gesamten Bildschirm des Geräts einnimmt, andere Widgets darin leben und Ereignisse über Benutzerinteraktion empfängt.
Wenn der Benutzer die Anwendung aktiviert, wird in der Aktivität ein onResume-Ereignis ausgelöst. Drückte die Home-Taste - Aktivität verschwand, aber zuvor erhielt er das Ereignis onPause. Bisher ist alles logisch. Was passiert, wenn ein Benutzer den Gerätebildschirm um 90 Grad dreht? Sie werden es nie erraten: Das Betriebssystem beendet die Aktivität und erstellt sie neu! Wenn Sie eine Aktivität deinstallieren, werden alle darin enthaltenen Komponenten gelöscht. Dies bedeutet, dass Sie speziellen Code schreiben müssen, der den Status von Widgets in der Aktivität (Bildlaufposition, ausgewähltes Textstück, Status von Kontrollkästchen) speichert und diese nach der Neuerstellung des übergeordneten Elements wiederherstellt. Android erledigt einen Teil dieser Arbeit für Sie, aber nicht alle.
Es ist schwer zu verstehen, was Architekten zu einer solchen Entscheidung veranlasst hat. Später entschied jemand, dass es irgendwie hässlich aussah, und fügte denselben Parameter hinzu, der das Standardverhalten des Systems unterdrückt. Anstatt Activity neu zu erstellen, kann das Betriebssystem jetzt die spezielle onConfigurationChanged-Methode aufrufen. Diese „Lösung“ sieht aus wie ein schmutziger Hack und hat das Problem nur verschärft, da in der Android-Dokumentation angegeben ist,
dass „diese Technik als extreme Maßnahme angesehen werden sollte und für die meisten Anwendungen nicht empfohlen wird“.
Fragmentierte Fragmente
Ursprünglich wurde das Android-Betriebssystem für Telefone erstellt. Mit dem Aufkommen der Tablets entschied das Android-Team zu Recht, dass die Elemente der Benutzeroberfläche, die zuvor aufgrund der geringen Größe des Telefonbildschirms durch verschiedene Aktivitäten voneinander getrennt waren, jetzt auf dem großen Bildschirm des Tablets koexistieren können. (Beispiel: Die Liste der Briefe und die E-Mail-Adresse des Telefons befanden sich in zwei verschiedenen Aktivitäten, und auf dem Tablet wurde ein Bildschirm gemeinsam genutzt.) Ja, das Problem ist: Mehrere Aktivitäten können nicht gleichzeitig mit dem Benutzer interagieren. Daher ist ein anderer Mechanismus zur Wiederverwendung von Komponenten erforderlich, und es wurde eine Lösung gefunden: Fragmente erschienen (Fragment).
Per
Definition repräsentiert ein Fragment ein Verhalten oder einen Teil einer Benutzeroberfläche in einer Aktivität. Alles klar. In der Anleitung zur Verwendung von Fragmenten finden wir jedoch den Abschnitt „Hinzufügen eines Fragments ohne Benutzeroberfläche“. WAS? Es stellt sich heraus, dass ein Fragment möglicherweise kein eigenes Fenster zum Rendern hat!
Aber was passiert mit Fragmenten beim Drehen des Bildschirms? Android-Entwickler haben darüber nachgedacht. Wenn Sie eine Aktivität neu erstellen, werden auch alle Fragmente automatisch neu erstellt. Bei sorgfältiger Prüfung verfügt das Fragment jedoch über die Methode "setRetainInstance". Wenn Sie es aufrufen, wird dieses Fragment während der Runden nicht gelöscht und wiederhergestellt.
Schlank und konsequent kann dieses Konzept nicht genannt werden.
Asynchroner Betrieb
Der Ereignisverarbeitungsthread kann nicht durch lange Vorgänge blockiert werden. Daher sollte der Datenaustausch mit der Festplatte und dem Netzwerk in anderen Threads asynchron erfolgen. Android bietet bis zu vier Mechanismen für den asynchronen Betrieb: AsyncTask, Service, IntentService, Thread. Entwickler werden ermutigt, herauszufinden, welches für sie am besten geeignet ist. Die Auswahl ist nicht trivial: Wenn die Anwendung beispielsweise AsyncTask gestartet hat und der Benutzer den Bildschirm während der Ausführung gedreht hat, kann AsyncTask nach Abschluss der Arbeit keine neue (nach dem Abbiegen erstellte) Aktivität finden. Und keiner der vier Mechanismen implementiert eine einfache Logik: Wenn der Benutzer den zweiten asynchronen Prozess gestartet hat, ohne auf die Ergebnisse des ersten zu warten, und der erste noch früher beendet wurde, ist es angebracht, seine Ergebnisse in der Benutzeroberfläche zu ignorieren und nicht wiederzugeben.
Es wird das Erscheinungsbild zahlreicher Bibliotheken deutlich - Datenbusse zum Organisieren asynchroner Arbeit in einer Android-Anwendung.
Neustart der Anwendung nach OOM
Wenn Android keinen freien Arbeitsspeicher mehr hat, kann das Betriebssystem jeden Vorgang abschließen. Wenn der Benutzer die Anwendung in Zukunft aktiviert, versucht das System, ihren Status wiederherzustellen. Theoretisch sollte dies für den Entwickler unsichtbar sein (wenn er die onSaveInstanceState-Methode in allen Komponenten implementiert hat): Das System selbst erstellt den Aktivitätsstapel wie beim erzwungenen Stopp neu. Wenn die Initialisierung jedoch einige Zeit in Anspruch nimmt (z. B. Laden von Caches von der Festplatte oder vom Netzwerk), ist es korrekt, dem Benutzer eine Warteanzeige anzuzeigen. Es stellt sich heraus, dass Sie beim Erstellen einer Aktivität weiterhin den „Kaltstart“ überwachen und die Initialisierung manuell durchführen müssen. Ganz zu schweigen davon, dass beim Neustart kein Thread und keine AsyncTask wiederhergestellt werden.
Wie wahrscheinlich ist es in der Praxis, dass der Speicher knapp wird? In der Regel tritt diese Situation bei der Verarbeitung hochauflösender Bilder auf. Beispielsweise belegt ein Bild mit einer Größe von 2048 x 1536 12 MB im Speicher eines Bitmap-Objekts. Die für die Anwendung verfügbare Speichermenge hängt vom jeweiligen Gerätemodell ab und ist manchmal sehr gering (64 MB oder 128 MB). Da der Garbage Collector in allen Android-Versionen vor 8.0 nicht komprimiert wurde, selbst wenn Sie über 100 MB freien Speicher verfügen, dieser jedoch in Blöcke von 10 MB unterteilt ist, führte der Versuch, Speicher für ein solches Bild zuzuweisen, zu einem Absturz der Anwendung.
Ewige Müllabfuhr
Sobald unser Benutzer bemerkte, dass die Anwendung beim Scrollen durch lange Listen „verzögert“ war - alle 2-3 Sekunden wurde die Animation für kurze Pausen (200-300 ms) angehalten und dann fortgesetzt. Die Analyse ergab, dass die Speicherbereinigung in der Anwendung verdächtig häufig beginnt. Es stellte sich heraus, dass die Standard-HashMap-Klasse (mit der wir ein Java-Objekt anhand seiner ID abrufen) in unserem Fall ineffizient ist: Wir müssen für jeden Schlüssel ein Wrapper-Objekt erstellen, das eine Ganzzahl (int) ist. Daher nimmt die Anzahl der Speicherzuweisungen ohne Nutzen zu. Die Lösung bestand darin, auf einen speziellen SparseArray-Container umzusteigen (nur in Android verfügbar, nicht in der Standard-Java-Plattform), wodurch der Druck auf den Garbage Collector sofort verringert wurde.
Was hat die Verzögerung in der Animation damit zu tun? Tatsache ist, dass der Garbage Collector in Android alle Threads während seines Betriebs gestoppt hat, einschließlich des Haupt-Threads, der am Rendern beteiligt ist. In Versionen von Android ab 5.0 wird eine andere virtuelle Maschine (ART anstelle von Dalvik) und ein anderer Speicherbereinigungsalgorithmus verwendet, wodurch die Animation immer weniger angehalten wird. (Informationen zum Vergleichen der Garbage Collection-Zeit finden Sie
hier .)
Dokumente anzeigen
Wenn Sie eine Dokumentvorschau in Ihre Anwendung einfügen möchten, müssen Sie enttäuschen: In Android gibt es keine integrierte Komponente, die Word-, Excel- und Powerpoint-Dateien anzeigen kann. Ganz zu schweigen von ZIP-Archiven oder PDF-Dokumenten. Ausweg? Erzwingen Sie, dass der Benutzer Anwendungen von Drittanbietern installiert, um jeden Dateityp anzuzeigen, oder verwenden Sie die WebView-Komponente (eigentlich einen Browser). In beiden Fällen wird beim Aufnehmen der Datei durch den Benutzer eine externe Anwendung gestartet und einige Szenarien können einfach nicht implementiert werden, z. B. ein Bild-Feed: Es gibt keine einfache Möglichkeit, WebView in einen Standard-ViewPager zu integrieren.
Was weiter
Einige Studien behaupten, dass die
Komplexität der Android-Entwicklung relativ hoch ist . Unsere Erfahrung zeigt, dass es für einen kompetenten Entwickler einfach ist, sich an die Funktionen dieser Plattform zu gewöhnen. Und obwohl Android heute das
beliebteste mobile Betriebssystem der Welt ist , kann sich die Situation in 5-10 Jahren radikal ändern.
In den letzten Jahren hat Google heimlich das
neue Fuchsia OC mit einem grundlegend anderen (im Vergleich zu modernen Betriebssystemen) Sicherheitsmodell entwickelt. Es wird wahrscheinlich
Dart als Hauptprogrammiersprache und das
Flutter- Framework als Hauptmethode zum Erstellen von Anwendungen verwenden. Gerüchten zufolge
kann Fuchsia Android und ChromeOS auf allen Geräten
ersetzen . Wenn Google dies tut, bietet das Unternehmen wahrscheinlich native Unterstützung für Anwendungen, die für Android geschrieben wurden (wie Microsoft beim Wechsel von DOS zu Windows). Daher können Sie sich bisher keine Sorgen machen und weiterhin Fachwissen in Android sammeln. Und für diejenigen, die in die Zukunft schauen möchten, können Sie Flutter herunterladen und hier damit spielen.