1. ホーム
  2. android

[解決済み] Android新推奨アーキテクチャにおけるBoundService + LiveData + ViewModelのベストプラクティス

2023-07-30 11:07:32

質問

Android Services をどこに配置するか、かなり悩んでいます。 Android 推奨アーキテクチャ . 多くの可能な解決策を思いつきましたが、どれが最良のアプローチであるかについて私の心を決めることはできません。

私は多くの研究を行いましたが、有用なガイドラインやチュートリアルを見つけることができませんでした。私のアプリのアーキテクチャのどこにサービスを配置するかについて私が見つけた唯一のヒントは、@JoseAlcerreca からのものです。 ミディアム ポスト

理想的には、ViewModelsはAndroidについて何も知るべきではないでしょう。これは、テスト容易性、リークセーフ、およびモジュール性を向上させます。一般的な経験則では、ViewModels に android.* インポートがないことを確認します (android.arch.* のような例外を除く)。同じことが、プレゼンターにも当てはまります。

それによると、私は Android Services を Architecture Components 階層の一番上、Activity や Fragments と同じレベルに配置する必要があります。これは、AndroidサービスがAndroidフレームワークの一部であるため、ViewModelsがそれらについて知ってはならないからです。

さて、私のシナリオを簡単に説明しますが、パノラマをより明確にするためだけで、この特定のシナリオに対する答えが欲しいわけではありません。

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

私が思いつく3つの異なるアーキテクチャを紹介します。

AndroidService内のLiveData

UPDATE: これは私が個人的に当時行った方法で、うまく機能し、比較的早く終わらせることができたからです。しかし、私は Jeel Vankhede による更新された回答に従ってより多くの "idiomatic" な実装を行うことを提案します。

  • 接続状態とBluetoothデバイスからの重量測定値を含むLiveDataは、BluetoothServiceの中にあります。 Bluetooth Deviceから送られてくる重量測定値を含むLiveDataは、BluetoothServiceの中にあります。
  • フラグメントは、BluetoothServiceの操作(例えばscanDevices)をトリガーすることができます。
  • フラグメントは、接続の状態に関するLiveDataを監視します。 それに応じてUIを調整する(例えば、接続状態であればボタンを有効にする を有効にするなど)。
  • Fragmentは、新しい体重測定のLiveDataを観測します。新しい体重測定がBluetoothDeviceから来た場合、Fragmentは自身のViewModelに新しいデータを保存するように指示します。これはRepositoryクラスを介して行われます。

フラグメントとAndroidServiceの間でViewModelを共有します。

  • FragmentはBluetoothServiceの操作をトリガーすることができます(例えばscanDevices)。
  • BluetoothServiceが共有ViewModelのBluetooth関連LiveDataを更新する。
  • Fragmentは、自身のViewModelのLiveDataを観測します。

サービスViewModel

  • FragmentはBluetoothServiceの操作をトリガーすることができます(例えばscanDevices)。
  • BluetoothServiceは自身のViewModelにあるBluetooth関連のLiveDataを更新する。
  • Fragmentは、自身のViewModelとBluetoothServiceのViewModelにあるLiveDataを観測します。

BoundServices は Android Framework の一部であり、Android OS によって管理され、他の Activity や Fragment にバインドされるため、アーキテクチャの上に配置して Activity/Fragment と同様に扱うべきであると確信しています。その場合、LiveData、ViewModels、Activity/Fragmentとどのようにやり取りするのがベストなのかわかりません。

データソースとみなすべきと考える人もいるかもしれませんが(私の場合、Bluetoothを使って体重計からデータを取得しているので)、私はこれは良い考えだとは思いません。 というのは、ここに書かれている :

<ブロッククオート

アプリのエントリポイント(アクティビティなど)を指定しないようにしましょう。 サービス やブロードキャストレシーバーなど、アプリのエントリポイントをデータのソースとして指定することは避けてください。その代わり、他のコンポーネントと連携して、そのエントリポイントに関連する そのエントリポイントに関連するデータのサブセットを取得するために、他のコンポーネントと調整するだけです。各アプリ 各アプリコンポーネントの寿命は短く、ユーザーのデバイスとのインタラクションや現在の全体的な健全性に依存します。 各アプリのコンポーネントは、ユーザーのデバイスとのやり取りやシステムの現在の全体的な状態に応じて、かなり短命です。

では最後に、私の質問です。

Android (Bound) Services をどこに配置すべきか、また、他のアーキテクチャ コンポーネントとどのような関係があるのでしょうか。これらの選択肢のどれかが良いアプローチなのでしょうか?

どのように解決するのですか?

更新しました。

Ibrahim Disouki氏からの提案を受けて (ありがとうございました) さらに掘り下げてみると、面白いことがわかりました 以下はその背景です。

O.P.です。 は、解決策を模索する "。 Android FrameworkのServiceコンポーネントは、Android Architecture Componentsの中でどのような位置づけにあるのでしょうか? となります。ということで、アウトザボックス(SDK)ソリューションをご紹介します。

と同じレベルにある Activity/Fragment . どうやって?それよりも、Service クラスを拡張するのであれば、まず LifecycleService . その理由は簡単で、以前はServiceの更新を受け取り、コンテキストに沿った操作を行うために、Activity/Fragmentのライフサイクルに依存する必要があったからです。しかし、今はそのようなことはありません。

LifecycleService という独自のライフサイクルレジストリ/メンテナーを持つようになりました。 ServiceLifecycleDispatcher と呼ばれる、サービスのライフサイクルを管理するレジストリ/メンテナがあります。 LifecycleOwner .

という条件を残しています。 これから先、あなたは ViewModel から LifecycleService を使用することで、自分自身のために操作を行うことができます。

O.P.のコンテキストでは、LifecycleServiceは、それを維持する能力を持つことができるようになりました。 ViewModel を維持し、後でアクティビティやフラグメントのような別のライフサイクル認識コンポーネントが、同じ ViewModel を消費/再利用して、特定の操作を行うことができます。

そうすることで、2つの異なる LifecycleOwner s (Activity & LifecycleServie)の2つがある状態です。 の間でビューモデルを共有できないことを意味します。 LifecycleService といったライフサイクルを意識したコンポーネントの間でビューモデルを共有できないことを意味します。もし、このアプローチが嫌なら、データが提供できるようになったときに、サービスからアクティビティやフラグメントにコールバックするような、古いコールバックのようなアプローチも良いでしょう。


削除しました。

(読み流さないことをお勧めします)

私の意見では サービス と同じレベルであるべきです。 アクティビティ/フラグメント というのは、Framework コンポーネント & ではないからです。 MVVM を実装していないためです。 ライフサイクルオーナ として扱われることはなく、Android Framework Component として扱われます。 データソース というのは、アプリケーションのエントリポイントになり得るからです。

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

ですから Android アーキテクチャ コンポーネント ? として扱えばいいと思います。 ライフサイクルオブザーバー .なぜなら、バックグラウンドで何をするにしても、そのライフサイクルについて考慮する必要があるからです。 LifecycleOwner .

なぜかというと、通常、私たちはこれを LifecycleOwner (アクティビティ/フラグメント) &を使って、UIから離れたところで長時間実行されるタスクを行うことができます。のように扱うことができるわけです。 ライフサイクルオブザーバー . このように、私たちのサービスをquot; ライフサイクルを意識したコンポーネント としました。


どのように実装するのですか?

  1. あなたの サービスクラス を実装し ライフサイクル・オブザーバ インターフェイスを実装します。

  2. サービスをバインドする際に Activity/Fragment にバインドするとき、サービスクラスのサービス接続の間に、 メソッドを呼び出すことによって、サービスを LifecycleObserver としてアクティビティに追加します。 getLifecycle().addObserver(service class obj)

  3. さて、サービスクラスでサービスからUIへのコールバックを提供するインターフェースを取り、データが変更されるたびに、サービスが少なくともライフサイクルイベントを持つかどうかをチェックします。 を作成します。 または 再開 を使ってコールバックを提供します。

このような方法で、私たちは LiveData をサービスから更新する必要はなく、さらに ViewModel (なぜサービスにそれが必要なのでしょうか?サービスのライフサイクルで生き残るために、設定の変更は必要ありません。また、VMの主なタスクは、ライフサイクル間でデータを保持することです。) .


補足: バックグラウンドでの動作が長いと思われる場合は WorkManager . このライブラリを使った後では、Servicesはもう非推奨とマークされるべきだと感じることでしょう! (単なる思いつき)