web-dev-qa-db-ja.com

BoundService + LiveData + ViewModelの新しいベストプラクティスAndroid推奨アーキテクチャ

新しい Androidが推奨するアーキテクチャ でAndroidサービスを配置する場所について考えるのに苦労しました。私は多くの可能な解決策を思いつきましたが、どれが最良のアプローチであるかについて決心することはできません。

私は多くの研究を行ったが、有用なガイドラインもチュートリアルも見つけられなかった。アプリアーキテクチャのどこにサービスを配置するかについて私が見つけた唯一のヒントは、@ JoseAlcerreca Medium post

理想的には、ViewModelsはAndroidについて何も知らないようにする必要があります。これにより、テスタビリティ、リークの安全性、モジュール性が向上します。一般的な経験則は、ViewModelsにAndroid。*のインポートがないことを確認することです(Android.Arch。*などの例外を除く)。同じことがプレゼンターにも当てはまります。

それによると、アクティビティコンポーネントと同じレベルで、Androidサービスをアーキテクチャコンポーネント階層の最上位に配置する必要があります。 AndroidサービスはAndroidフレームワークの一部であるため、ViewModelsはそれらについて知る必要がないためです。

ここで、シナリオを簡単に説明しますが、この特定のシナリオに対する答えが欲しいからではなく、パノラマを明確にするためだけです。

  • MainActivityに多くのフラグメントが含まれるAndroidアプリケーションがあり、それらはすべてBottomNavBarにまとめられています。
  • MyActivityとそのフラグメントの1つにバインドされたBluetoothServiceがあります(サービスにActivtyと同じライフサイクルを持たせたいのですが、フラグメントから直接やり取りしたいので)。
  • フラグメントはBluetoothServiceと対話して、2種類の情報を取得します。
    • Bluetooth接続の状態に関する情報。永続化する必要はありません。
    • Bluetoothデバイスからのデータ(これは体重計なので、この場合は体重と体組成)。永続化する必要があります。

ここに私が考えることができる3つの異なるアーキテクチャがあります:

AndroidService内のLiveDataLiveData inside Android Service Arch

  • 接続の状態とBluetoothデバイスからの重量測定値を持つLiveDataは、BluetoothService内にあります。
  • フラグメントは、BluetoothServiceで操作をトリガーできます(たとえば、scanDevices)
  • フラグメントは、接続の状態についてLiveDataを監視し、それに応じてUIを調整します(たとえば、状態が接続されている場合はボタンを有効にします)。
  • フラグメントは、新しい重量測定のLiveDataを監視します。 BluetoothDeviceから新しい重量測定値が取得されると、フラグメントは独自のViewModelに新しいデータを保存するよう指示します。 Repositoryクラスを介して行われます。

フラグメントとAndroidService間の共有ViewModelShared ViewModel Arch

  • フラグメントは、BluetoothServiceで操作をトリガーできます(たとえば、scanDevices)
  • BluetoothServiceは、共有ViewModelのBluetooth関連のLiveDataを更新します。
  • フラグメントは、独自のViewModelでLiveDataを監視します。

Service ViewModelService ViewMOdel Arch

  • フラグメントは、BluetoothServiceで操作をトリガーできます(たとえば、scanDevices)
  • BluetoothServiceは、独自のViewModelでBluetooth関連のLiveDataを更新します。
  • フラグメントは、独自のViewModelおよびBluetoothService ViewModelでLiveDataを監視します。

BoundServicesはAndroid Frameworkの一部であり、Androidによって管理されるため、アーキテクチャの上に配置してアクティビティ/フラグメントのように扱う必要があると確信しています。 OSおよびそれらは、他のアクティビティおよびフラグメントにバインドされています。その場合、LiveData、ViewModels、Activities/Fragmentsを操作する最善の方法はわかりません。

データソースとして考えるべきだと思う人もいるかもしれませんが(私の場合はBluetoothを使用してスケールからデータを取得しているため)、前の段落で述べたことすべてのため、これは良い考えではないと思いますそして特に ここで言うことから

アプリのエントリポイント(アクティビティ、サービス、ブロードキャストレシーバーなど)をデータのソースとして指定しないでください。代わりに、他のコンポーネントとのみ調整して、そのエントリポイントに関連するデータのサブセットを取得する必要があります。各アプリコンポーネントは、ユーザーのデバイスとのやり取りやシステムの全体的な現在の状態に応じて、寿命が短くなります。

だから、最後に、私の質問は次のとおりです。

Android(バウンド)サービスをどこに配置する必要があり、他のアーキテクチャコンポーネントとの関係は何ですか?これらの選択肢のいずれかが良いアプローチですか?

26
dglozano

私の意見では、Serviceと同じレベルにあるべきですActivity/Fragment、これはフレームワークコンポーネントであり、[〜#〜] mvvm [〜#〜ではないため]。ただし、そのため、サービスはLifecycleOwnerを実装せず、Android Framework Component、アプリケーションへのエントリポイントになる可能性があるため、データソースとして扱わないでください。

そのため、ここでのジレンマは、時々(あなたの場合)、Serviceはデータソースとして機能し、長時間実行されるタスクからUIにデータを提供します。

では、Android Architecture Componentに何があるべきでしょうか? LifecycleObserverとして扱うことができると思います。バックグラウンドで何をするにしても、LifecycleOwnerのライフサイクルについて考慮する必要があるためです。

どうして?なぜなら、通常はLifecycleOwner(Activity/Fragments)にバインドして長時間実行するタスクを行うためですUIから。したがって、LifecycleObserverのように扱うことができます。このようにして、サービスを「ライフサイクル対応コンポーネント」として作成しました!


どのように実装できますか?

  1. service classを取り、それにLifecycleObserverインターフェースを実装します。

  2. サービスを_Activity/Fragment_にバインドする場合、サービスクラスのサービス接続中に、メソッドgetLifecycle().addObserver(service class obj)を呼び出して、サービスをLifecycleObserverとしてアクティビティに追加します。

  3. 次に、サービスクラスのインターフェイスを使用して、サービスからUIにコールバックを提供し、データが変更されるたびに、サービスに少なくともLifecycleイベントがあるかどうかを確認します create または resume =コールバックを提供します。

このように、サービスからの更新にLiveDataを必要とせず、ViewModelも必要ありません(なぜサービスに必要なのですか?サービスのライフサイクルで生き残るためには、構成の変更が必要です。そして、VMの主なタスクは、ライフサイクル間でデータを構成することです)

私はあなたのためにそれを明確にしたことを願っています!

6
Jeel Vankhede

Androidサービスを使用しながら、サービスとの直接の接触を回避する1つの方法は、インターフェースオブジェクトを使用することです。これはインターフェース分離の「I」頭字語では、[〜#〜] solid [〜#〜]。小さな例を次に示します。

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();
    }
}

上記のパラダイムを考えると、POJOコードを含むパッケージの外部にあるパッケージにサービスを自由に配置できます。サービスを配置するための「正しい」場所はありませんが、サービスを配置する場所は間違いなくあります(たとえば、POJOコードが行く場所)。

0
Grant Park