wake-up-neo.net

BoundService + LiveData + ViewModel Best Practice in der von Android empfohlenen Architektur

Ich habe viel darüber nachgedacht, wo ich Android Services in der neuen von Android empfohlenen Architektur platzieren könnte. Ich habe viele mögliche Lösungen gefunden, aber ich kann mich nicht entscheiden, welche die beste Lösung ist.

Ich habe viel recherchiert und konnte keine nützliche Richtlinie oder Anleitung finden. Der einzige Hinweis, den ich gefunden habe, um den Service in meiner App-Architektur zu platzieren, ist dieser, von @JoseAlcerreca Medium post

Idealerweise sollten ViewModels nichts über Android wissen. Dies verbessert die Testbarkeit, die Ausfallsicherheit und die Modularität. Als Faustregel gilt, dass in Ihren ViewModels keine Android. * -Importe (mit Ausnahmen wie Android.Arch. *) Vorhanden sind. Gleiches gilt für Moderatoren.

Dementsprechend sollte ich meine Android-Dienste auf der obersten Ebene meiner Architekturkomponenten-Ebene auf derselben Ebene wie meine Aktivitäten und Fragmente platzieren. Das liegt daran, dass Android-Services Teil des Android-Frameworks sind, sodass ViewModels davon nichts wissen sollte.

Nun werde ich kurz mein Szenario erläutern, aber nur um das Panorama klarer zu machen, nicht weil ich eine Antwort für dieses spezielle Szenario möchte.

  • Ich habe eine Android-Anwendung, die eine MainActivity mit vielen Fragmenten enthält, die alle in einer BottomNavBar miteinander verbunden sind.
  • Ich habe einen Bluetooth-Dienst, der an myActivity und eines seiner Fragmente gebunden ist (weil ich möchte, dass der Dienst den gleichen Lebenszyklus hat wie der Activty-Server, aber ich möchte auch direkt von meinem Fragment aus damit interagieren).
  • Das Fragment interagiert mit dem BluetoothService, um zwei Arten von Informationen abzurufen:
    • Informationen zum Status der Bluetooth-Verbindung. Muss nicht beharrlich sein.
    • Daten, die vom Bluetooth-Gerät stammen (es handelt sich hierbei um eine Waage, in diesem Fall also Gewicht und Körperzusammensetzung). Muss beständig bleiben.

Hier sind die 3 verschiedenen Architekturen, die ich mir vorstellen kann:

LiveData in AndroidService LiveData inside Android Service Arch

  • Die LiveData mit dem Status der Verbindung und mit den Gewichtungsmessungen, die vom Bluetooth-Gerät stammen, befinden sich im BluetoothService.
  • Das Fragment kann Operationen im BluetoothService auslösen (zum Beispiel scanDevices)
  • Das Fragment beobachtet die LiveData über den Verbindungsstatus Und passt die Benutzeroberfläche entsprechend an (aktivieren Sie beispielsweise eine Schaltfläche, wenn der Status Verbunden ist).
  • Das Fragment beobachtet die LiveData der neuen Gewichtsmessungen. Wenn eine neue Gewichtsmessung vom Bluetooth-Gerät kommt, teilt das Fragment seinem eigenen ViewModel mit, die neuen Daten zu speichern. Dies erfolgt über eine Repository-Klasse.

Shared ViewModel zwischen Fragment und AndroidService Shared ViewModel Arch

  • Das Fragment kann Operationen im BluetoothService auslösen (zum Beispiel scanDevices)
  • Der BluetoothService aktualisiert die Bluetooth-bezogenen LiveData im freigegebenen ViewModel.
  • Das Fragment beobachtet die LiveData in einem eigenen ViewModel.

Service ViewModel Service ViewMOdel Arch

  • Das Fragment kann Operationen im BluetoothService auslösen (zum Beispiel scanDevices)
  • Der BluetoothService aktualisiert die Bluetooth-bezogenen LiveData in einem eigenen ViewModel.
  • Das Fragment beobachtet die LiveData in einem eigenen ViewModel und dem BluetoothService ViewModel.

Ich bin mir ziemlich sicher, dass ich sie auf die Architektur setzen und sie wie eine Aktivität/ein Fragment behandeln sollte, da BoundServices Teil des Android Frameworks sind. Sie werden vom Android-Betriebssystem verwaltet und sind an andere Aktivitäten und Fragmente gebunden. In diesem Fall weiß ich nicht, wie Sie am besten mit LiveData, ViewModels und Aktivitäten/Fragmenten interagieren können.

Einige mögen denken, dass sie als DataSource betrachtet werden sollten (da in meinem Fall Daten über Bluetooth von einer Waage abgerufen werden), aber ich denke nicht, dass dies eine gute Idee ist, weil ich all das im vorherigen Absatz gesagt habe und besonders wegen dem, was es hier sagt

Vermeiden Sie das Festlegen der Einstiegspunkte Ihrer App, z. B. Aktivitäten services und Rundfunkempfänger - als Datenquellen. Stattdessen sollten sie sich nur mit anderen Komponenten abstimmen, um die .__ abzurufen. Teilmenge der Daten, die für diesen Einstiegspunkt relevant sind. Jede App Die Komponente ist je nach Interaktion des Benutzers eher kurzlebig. mit ihrem Gerät und dem aktuellen Gesamtzustand des Systems.

So ist meine Frage schließlich: 

Wo sollten wir unsere Android (gebundenen) Dienste platzieren und in welcher Beziehung stehen sie zu den anderen architektonischen Komponenten? Ist eine dieser Alternativen ein guter Ansatz?

13
dglozano

Meiner Meinung nach sollte Service auf dem gleichen Niveau sein wie Aktivität/Fragment , da es sich um die Framework-Komponente & nicht handelt [~ # ~] mvvm [~ # ~ ] . aber deswegen implementiert Service nicht LifecycleOwner und es ist Android Framework Component, Es sollte nicht als Datenquelle behandelt werden, da es ein Einstiegspunkt für die Anwendung sein kann.

Das Dilemma hier ist also, dass manchmal (In Ihrem Fall) der Dienst als Datenquelle fungiert, die Daten von einer lang laufenden Aufgabe an die Benutzeroberfläche liefert.

Also, was sollte es in Android Architecture Component sein? Ich denke, Sie können es als LifecycleObserver behandeln. Ganz gleich, was Sie im Hintergrund tun, Sie müssen den Lebenszyklus des LifecycleOwners berücksichtigen.

Warum? weil wir es normalerweise an LifecycleOwner (Aktivität/Fragmente) binden, um lang laufende Aufgaben zu erledigen aus der Benutzeroberfläche. Es kann also wie LifecycleObserver behandelt werden. So haben wir unseren Service als " Lifecycle aware component " gestaltet!


Wie können Sie es umsetzen?

  1. Nehmen Sie Ihre Serviceklasse und implementieren Sie LifecycleObserver Schnittstelle.

  2. Wenn Sie Ihren Dienst an Activity/Fragment Binden, fügen Sie Ihren Dienst während der Verbindung Ihrer Dienstklasse zu Ihrer Aktivität als LifecycleObserver hinzu, indem Sie die Methode getLifecycle().addObserver(service class obj) aufrufen.

  3. Nehmen Sie nun eine Schnittstelle in der Serviceklasse, um einen Rückruf vom Service an Ihre Benutzeroberfläche bereitzustellen, und überprüfen Sie bei jeder Änderung Ihrer Daten, ob Ihr Service mindestens ein Lifecycle-Ereignis aufweist create oder resume = Rückruf mit.

Auf diese Weise brauchen wir nicht LiveData, um vom Dienst auf zu aktualisieren, und auch nicht ViewModel (Warum brauchen wir es für den Dienst? Wir brauchen es nicht Konfigurationsänderungen sind erforderlich, um im Servicelebenszyklus zu überleben. Die Hauptaufgabe von VM besteht darin, Daten zwischen den Lebenszyklen zusammenzuführen) .

Hoffe, ich habe es dir klar gemacht!

6
Jeel Vankhede

Eine Möglichkeit, den direkten Kontakt mit einem Android-Dienst zu vermeiden, während er dennoch verwendet werden kann, ist über ein Schnittstellenobjekt. Dies ist Teil des "I" für Interface Segregation im Akronym SOLID. Hier ist ein kleines Beispiel:

public interface MyFriendlyInterface {
    public boolean cleanMethodToAchieveBusinessFunctionality();
    public boolean anotherCleanMethod();
}

public class MyInterfaceObject implements MyFriendlyInterface {
    public boolean cleanMethodToAchieveBusinessFunctionality() {
        BluetoothObject obj = Android.Bluetooth.nastySubroutine();
        Android.Bluetooth.nastySubroutineTwo(obj);
    }

    public boolean anotherCleanMethod() {
        Android.Bluetooth.anotherMethodYourPresentersAndViewModelsShouldntSee();
    }
}

public class MyViewModel {
    private MyFriendlyInterface _myInterfaceObject;

    public MyViewModel() {
        _myInterfaceObject = new MyInterfaceObject();
        _myInterfaceObject.cleanMethodToAchieveBusinessFunctionality();
    }
}

In Anbetracht des obigen Paradigmas können Sie Ihre Services in einem Paket außerhalb Ihrer Pakete mit POJO-Code platzieren. Es gibt keinen "richtigen" Ort für Ihre Dienste - aber es gibt definitiv FALSCHE Orte, an denen Sie sie platzieren können (beispielsweise wo Ihr POJO-Code hingeht). 

0
Grant Park