通信手段 - Intent
と EventBus
と RemoteShareFlow
ここでは、 Intent、 EventBus、RemoteShareFlow のそれぞれの特徴、利点、制約、具体的な使用例を挙げたうえで、詳細な使い分けを説明します。このような設計選択は、アプリケーションの効率性や拡張性に直接影響するため、重要です。
1. Intent
特徴
-
Androidフレームワークの基本的な通信手段:
- コンポーネント間(アクティビティ、サービス、ブロードキャストレシーバーなど)の通信やアクション実行をトリガー。
-
明示的 (Explicit) Intent と 暗黙的 (Implicit) Intent に分かれる:
- 明示的Intent: 起動対象が明確(例: 特定のアクティビティやサービス)。
- 暗黙的Intent: 起動対象をOSが判断(例: 他アプリでファイルを開く)。
利点
-
システム統合:
- ブロードキャストを利用してシステムイベント(例: ネットワーク接続変更、バッテリー低下)を送受信可能。
-
シンプル:
- キーバリュー形式で簡単にデータを渡せる。
-
サービス/アクティビティ操作:
- コンポーネントの起動や停止をトリガー。
制約
-
状態管理に不向き:
- Intentは状態を維持することができず、一度送信されたら終了。
-
大量データの送受信は非効率:
- Intentに格納できるデータ量は制限があり、メモリ消費も大きい。
使用例
-
アクティビティ遷移:
val intent = Intent(this, SecondActivity::class.java) intent.putExtra("EXTRA_KEY", "value") startActivity(intent)
-
サービスの開始:
val intent = Intent(this, MyService::class.java) startService(intent)
2. EventBus
特徴
-
パブリッシュ・サブスクライブ型通信:
- イベントを発行元(Publisher)からリスナー(Subscriber)に非同期で伝達。
-
ローカル通信に特化:
- アプリ内でのUIコンポーネント間通信やイベント通知に使用。
利点
-
シンプルな設計:
- イベントを発行・購読する仕組みが軽量かつ簡潔。
-
リアルタイム性:
- サブスクライバが即座にイベントを受け取る。
-
疎結合:
- 発行元と受信先が直接依存しない設計。
制約
-
大規模システムでは混乱の原因:
- イベント数や種類が増えると、依存関係が不明瞭になる可能性がある。
-
ローカル通信限定:
- プロセス間通信には対応していない。
使用例
-
UIの状態更新:
EventBus.emit(MyEvent(data = "Update")) EventBus.observe<MyEvent> { event -> updateUI(event.data) }
-
センサーデータの通知:
- センサーからリアルタイムで取得したデータをUIに反映。
3. RemoteShareFlow
特徴
-
プロセス間通信 (IPC) を活用した双方向通信:
-
Messenger
を用いて、サービスとクライアント間でデータを送受信。
-
-
状態とイベントの管理:
-
SharedFlow
を使ったリアルタイムなデータフロー。
-
利点
-
サービス依存型の状態管理:
- サービスの状態をリモートで監視し、動的に更新。
-
双方向通信:
- コマンド送信とその結果のレスポンス受信を同一のデータフローで管理。
-
長期タスクに最適:
- フォアグラウンドサービスやバックグラウンド処理と強く連携。
制約
-
実装が複雑:
- Messengerの設定やライフサイクル管理が必要。
-
IPCのためのセットアップが必要:
- クライアントとサービスをバインドする仕組みが前提。
使用例
-
サービス状態のリクエスト:
remoteSharedFlow.emit(StrokeActivityStateAction())
-
リアルタイムイベントの受信:
remoteSharedFlow.flow().collect { event -> handleRemoteEvent(event) }
使い分けの指針
Intentの適用領域
- アクティビティ遷移、サービス開始、またはシステムイベントを処理する場合に使用。
- 一度限りのデータ送信や明確なアクションが必要なケースに最適。
EventBusの適用領域
- アプリ内の軽量で迅速な通信が必要な場合に使用。
- ローカルなUI更新やセンサーデータの通知など、小規模のリアルタイムイベント。
RemoteShareFlowの適用領域
- バックグラウンドで動作するリモートサービスとの通信や、サービスの状態管理を必要とするケースに最適。
- 長期的にデータフローを監視したり、双方向で情報をやり取りする必要がある状況。
使い分けのまとめ
特徴 | Intent | EventBus | RemoteShareFlow |
---|---|---|---|
用途 | 明確なアクション、コンポーネント間通信 | ローカルなイベント通知、リアルタイム更新 | サービスとクライアント間の双方向通信 |
通信対象 | アクティビティ、サービス、ブロードキャスト | UI、アクティビティ内部 | フォアグラウンド/バックグラウンドサービス |
リアルタイム性 | 低(アクションベース) | 高(即座に伝達) | 高(SharedFlowによる) |
状態管理 | 不可 | 不可 | 可能 |
通信のスコープ | アプリ内外のコンポーネント | アプリ内 | プロセス間通信 |
サービス - lib.service
lib.service
パッケージは、リモートサービスの接続とイベント処理を効率的に行うための仕組みを提供しています。
サービスとの通信
StrokeActivityStateAction
を例に、シーケンス図と詳細な説明を示します。この図は、イベントの生成、処理、レスポンスの流れを視覚的に表現します。
PlantUMLシーケンス図
説明
-
ユーザーのアクション:
- ユーザーがアクティビティ状態を確認したい場合、アプリに対してリクエストを送信します。
-
RequestStrokeActivityState
の呼び出し:- アプリケーションは
RequestStrokeActivityState.invoke()
を実行し、StrokeActivityStateAction
を生成します。 - このイベントオブジェクトはリモートサービスに送信されます。
- アプリケーションは
-
リモートサービスの処理:
-
RemoteEventService
はStrokeActivityStateAction
を受け取り、内部でDataRepository
に問い合わせを行い、現在のアクティビティ状態 (isInActivity
) を取得します。
-
-
レスポンス生成:
-
RemoteEventService
はisInActivity
の結果を基にStrokeActivityStateAction
を更新し、それをレスポンスとして返します。
-
-
レスポンスの処理:
- リモートサービスマネージャ (
RemoteServiceManager
) は返された状態データ (isInActivity
) をアプリに通知します。 - アプリケーション側でこのデータを利用して必要なアクションを実行できます。
- リモートサービスマネージャ (
活用場面
この流れは、アプリケーションがリモートサービスと連携して動作状況を管理する場面で役立ちます。例えば:
- アクティビティが現在アクティブかどうかを確認する。
- 状態に応じてUIやロジックを変更する。
フォアグランドサービスの起動と停止
次の図は、サービスの開始・停止時に関与する主要な要素を示します。
説明
-
起動:
-
launchServiceInForeground()
を呼び出すことで、通知チャンネルの生成と通知の表示を通じてサービスがフォアグラウンドモードに移行します。 -
RemoteEventService
内でstartForeground(notificationId)
が実行されます。
-
-
停止:
-
terminateForegroundService()
を呼び出すことで、通知がクリアされ、サービスはバックグラウンドまたは終了状態になります。 -
RemoteEventService
内でstopForeground()
とstopSelf()
が実行されます。
-
全体の機能概要
1. リモート通信を支える主要な仕組み
-
RemoteSharedFlow:
- リモートイベントの双方向通信を可能にする。
- コルーチンや
SharedFlow
を使用して非同期データストリームを提供。 - サービスのバインディング (
bindService
) と解除 (unbindService
) を簡単に管理。
2. サービスライフサイクル管理
-
RemoteEventService:
- Androidの
LifecycleService
を拡張し、リモートイベントの処理を統一的に管理。 - フォアグラウンドサービスとして動作し、ユーザー通知を表示する機能。
- イベントストリーム (
flow
) をリアルタイムで監視し、カスタムロジックを実行可能。
- Androidの
3. ユーティリティクラスでの一元管理
-
RemoteServiceManager:
- リモートサービス接続 (
connectToRemoteService
) と切断 (disconnectFromRemoteService
) を一元管理。 - サービスコマンドを送信 (
sendServiceCommand
) し、アクション (ACTION_FOREGROUND_START
など) を実行可能。 - 他のクラスやコンポーネントが簡単にリモート通信を利用できるAPIを提供。
- リモートサービス接続 (
4. 拡張機能と便利なヘルパーメソッド
-
背景でのイベント処理:
-
observeServiceActions
でリモートイベントの監視。 -
dispatchServiceEvent
を使ってリモートイベントの送信。
-
-
フォアグラウンドサービス操作:
-
launchServiceInForeground
でフォアグラウンドサービス開始。 -
terminateForegroundService
でフォアグラウンドサービス終了。
-
利用の全体的な流れ
-
サービス接続:
-
startServiceConnection
を呼び出してサービスを接続。
-
-
イベント処理:
-
observeServiceActions
でイベントを監視し、必要に応じてカスタムロジックを追加。 -
dispatchServiceEvent
でイベントをリモートサービスに送信。
-
-
フォアグラウンド化:
- 必要に応じて
launchServiceInForeground
でフォアグラウンドサービスを起動し、継続的に動作させる。
- 必要に応じて
-
サービス終了:
-
terminateForegroundService
とstopServiceConnection
を呼び出してサービスを終了。
-
データ処理の関連図
モジュール間の通信
モジュール間の通信は、EventBusもしくはIntentを通じて行います。
DataProcessorとの通信(EventBus)
DataProcessor
との通信は、EventBus
を使用します。
サービス間の通信(Usecaseパターン)
Activity
, Notification
, Service
間の通信(制御)は、IntentやFlowを使用します。
Usecase
パターンで、通信の実装を隠蔽します。
リポジトリパターン - Service/Repository/Procec
全体の仕組み概要
-
DataSource:
- センサーやファイルシステムなど、外部リソースに直接アクセスし、データを取得します。
- 取得したデータを非同期ストリームとして提供し、
Flow<T>
を返します。
-
DataProcessor:
- 一つまたは複数の
DataSource
を管理・制御します。 - 必要なデータを統合・加工し、データ処理のロジックを定義します。
-
EventBus
を通じてイベントの送受信を行います。
- 一つまたは複数の
-
DataRepository:
-
DataProcessor
をさらに管理・制御します。 - アプリケーションの他の部分と連携し、データの取得、処理、提供を統括します。
-
start
やstop
を通じてデータ処理タスクのライフサイクルを管理します。
-
repo
service
lib
イベントバス - lib.event, lib.eventbus
lib.event
パッケージは、SensingEvent
とその派生クラス SensingData
、SensingAction
、SensingParam
を提供し、これらは CsvSerializable
を継承してCSV形式にシリアライズ可能です。
また、lib.eventbus
パッケージは、イベントの発行と監視を行う EventBus
クラス、イベントハンドラのインターフェース EventBusHandler
、SensingEvent
型のイベントを扱う SensingEventBusHandler
とその実装 SensingEventBusHandlerImpl
、およびインスタンス生成を行う EventBusFactory
を含みます。
クラス図の説明
-
EventBus クラス
-
EventBus
はイベントを管理するクラスです。 - 内部に
MutableSharedFlow<Any>
型の_events
を持ち、外部にはSharedFlow<Any>
型のevents
を公開しています。 -
emit
メソッドでイベントを発行し、observe
メソッドでイベントを監視します。
-
-
EventBusHandler インターフェース
- ジェネリック型
T
を持つイベントハンドラのインターフェースです。 -
emitEvent
メソッドでイベントを発行し、observeEvent
メソッドでイベントを監視します。
- ジェネリック型
-
SensingEventBusHandler インターフェース
-
EventBusHandler<SensingEvent>
を継承したインターフェースです。 -
SensingEvent
型のイベントを扱います。
-
-
SensingEventBusHandlerImpl クラス
-
SensingEventBusHandler
インターフェースを実装したクラスです。 -
CoroutineScope
を使用して非同期にイベントを発行および監視します。
-
-
EventBusFactory オブジェクト
-
SensingEventBusHandler
のインスタンスを生成するファクトリクラスです。 - カスタムプロバイダを設定したり、リセットしたりするメソッドを持ちます。
-
-
SensingEvent クラス
-
CsvSerializable
を継承した抽象クラスです。 - タイムスタンプを持つイベントの基底クラスです。
-
-
SensingData, SensingAction, SensingParam クラス
-
SensingEvent
を継承した抽象クラスです。 - それぞれ異なる種類のセンシングイベントを表します。
-
-
CsvSerializable クラス
- フィールド値をCSV形式に変換するための抽象クラスです。
-
toFieldValues
メソッドを持ち、クラスのフィールド値をリストとして返します。
シリアライゼーション - lib.serialization
lib.serialization
は、CsvSerializableを継承したデータクラスをCSV(文字列)にシリアライズしたりデシリアライズしたりするパッケージです。
クラス図の説明
-
CsvSerializable クラス
- 抽象クラスであり、
toFieldValues
メソッドを持っています -
ClassTag
アノテーションを持っています
- 抽象クラスであり、
-
ClassTagRegistry オブジェクト
- クラスとタグのマッピングを管理するためのオブジェクトです
- クラスとタグのマッピングを保持する2つのマップ (
classToTagMap
とtagToClassMap
) を持っています - クラスとタグを登録・取得するためのメソッドを提供します
-
CsvHandler オブジェクト
- CSVシリアライズとデシリアライズを行うためのオブジェクトです
- クラスを登録するメソッド (
addCsvSerializable
) や、オブジェクトをCSVに変換するメソッド (convertObjectToCsv
)、CSVをオブジェクトに変換するメソッド (convertCsvToObject
) を持っています - 型に基づいて値を解決するためのリゾルバ (
TypeBasedValueResolver
) を追加するメソッドを持っています
関係
-
CsvHandler
はClassTagRegistry
を使用してクラスの登録やタグの取得を行います -
CsvHandler
はCsvSerializable
を使用してオブジェクトのシリアライズとデシリアライズを行います