[解決済み] Android新推奨アーキテクチャにおけるBoundService + LiveData + ViewModelのベストプラクティス
質問
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を観測します。
- 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; ライフサイクルを意識したコンポーネント としました。
どのように実装するのですか?
-
あなたの サービスクラス を実装し ライフサイクル・オブザーバ インターフェイスを実装します。
-
サービスをバインドする際に
Activity/Fragment
にバインドするとき、サービスクラスのサービス接続の間に、 メソッドを呼び出すことによって、サービスを LifecycleObserver としてアクティビティに追加します。getLifecycle().addObserver(service class obj)
-
さて、サービスクラスでサービスからUIへのコールバックを提供するインターフェースを取り、データが変更されるたびに、サービスが少なくともライフサイクルイベントを持つかどうかをチェックします。 を作成します。 または 再開 を使ってコールバックを提供します。
このような方法で、私たちは
LiveData
をサービスから更新する必要はなく、さらに
ViewModel
(なぜサービスにそれが必要なのでしょうか?サービスのライフサイクルで生き残るために、設定の変更は必要ありません。また、VMの主なタスクは、ライフサイクル間でデータを保持することです。)
.
補足: バックグラウンドでの動作が長いと思われる場合は
WorkManager
.
このライブラリを使った後では、Servicesはもう非推奨とマークされるべきだと感じることでしょう!
(単なる思いつき)
関連
-
[解決済み] 新しいAndroid Fragmentをインスタンス化するためのベストプラクティス
-
[解決済み] なぜフラグメントなのか、そしてアクティビティの代わりにフラグメントを使用するのはどんなときか?
-
[解決済み] フラグメントのインスタンス状態をバックスタックに正しく保存する方法は?
-
[解決済み] ViewPagerとフラグメント - フラグメントの状態を保存する正しい方法は何ですか?
-
[解決済み】フラグメント内のXML onClickを使用して、ボタンのクリックを処理する方法
-
[解決済み] AndroidでラジオボタンにOnClickListenerを設定するには?
-
[解決済み] wrap_contentでRelativeLayoutがフルスクリーンになってしまう
-
[解決済み] これはどういうことですか?失敗 [INSTALL_FAILED_CONTAINER_ERROR]?
-
[解決済み] WhatsAppでメッセージを送信する
-
[解決済み] AndroidのViewModelを手動でクリアする?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Android - タイトルバーに戻るボタンが表示される
-
[解決済み] Androidのソースコードにある@hideの意味とは?
-
[解決済み] Androidでマイナスマージンを使用するのは悪いことですか?
-
[解決済み] EclipseのAndroidプロジェクトにライブラリ/JARを追加する
-
[解決済み] Android: ランドスケープモード用の代替レイアウト xml
-
[解決済み] 非ActivityクラスでContextを取得する [重複].
-
[解決済み] アンドロイドでシェイクを検出するには?
-
[解決済み] アンドロイドのdatepickerダイアログで最大の日付を設定するには?
-
[解決済み] ProjectScopeServices に Factory タイプのサービスはありません。
-
[解決済み] Androidの環境設定。ユーザーが環境設定画面を使用していない場合、デフォルト値を読み込むにはどうすればよいですか?