このガイドでは、多くの健康&フィットネス アプリの共通の基盤となる、基本的なモバイル歩数計アプリの作成手順について説明します。
このワークフローでは、次の API が統合されています。
- モバイル デバイスから歩数データを取得するための SensorManager。
- Room: ローカル データ ストレージ。
- デバイス上の健康とフィットネスに関するデータを保存して共有するための ヘルスコネクト。
データの読み取りと必要なツールに関するサポートについては、Android Sensor Manager を使用してモバイル デバイスから歩数をトラッキングするをご覧ください。
ヘルスコネクトを使用するための開発環境をまだ設定していない場合は、スタートガイドの手順に沿って設定してください。
ハンドヘルド デバイスで権限をリクエストする
エクササイズ データを取得する前に、適切な権限をリクエストして付与されている必要があります。
ベスト プラクティスとして、必要な権限のみをリクエストし、ユーザーがアプリを起動したときにすべての権限を一度にリクエストするのではなく、状況に応じて各権限をリクエストするようにしてください。
多くのエクササイズ アプリが依存している歩数計センサーは、ACTIVITY_RECOGNITION
権限を使用します。AndroidManifest.xml ファイルにこの権限を追加します。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
</manifest>
実行時に ACTIVITY_RECOGNITION
権限をリクエストするには、権限のリクエストに関するドキュメントを参照してください。
マニフェストで FOREGROUND_SERVICE
を宣言する必要もあります。ACTIVITY_RECOGNITION
権限をリクエストしているので、FOREGROUND_SERVICE_TYPE_HEALTH
を宣言します。
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH"/>
フォアグラウンド サービスとフォアグラウンド サービスのタイプについて詳しくは、フォアグラウンド サービスをご覧ください。
ViewModel を使用して UI 状態を管理する
UI 状態を適切に管理するには、ViewModel を使用します。Jetpack Compose と ViewModel では、このワークフローについて詳しく説明しています。
また、Compose で UI を構築するうえで重要な部分である UI レイヤリングを使用すると、単方向データフローなどのアーキテクチャのベスト プラクティスに沿って開発できます。UI レイヤリングについて詳しくは、UI レイヤのドキュメントをご覧ください。
このサンプルアプリでは、UI に次の 3 つの基本状態があります。
- 読み込み中: 回転する円が表示されます。
- コンテンツ: 今日の歩数に関する情報が表示されます。
- エラー: 問題が発生したときにメッセージを表示します。
ViewModel
は、これらの状態を Kotlin の Flow
として公開します。シールクラスを使用して、可能な状態を表すクラスとオブジェクトを格納します。
class TodayScreenViewModel(...) {
val currentScreenState: MutableStateFlow<TodayScreenState> = MutableStateFlow(Loading)
[...]
}
sealed class TodayScreenState {
data object Loading : TodayScreenState()
data class Content(val steps: Long, val dailyGoal: Long) : TodayScreenState()
data object Error: TodayScreenState()
}
Compose UI は、この Flow
を Compose の State
として収集し、処理します。
val state: TodayScreenState = todayScreenViewModel.currentScreenState.collectAsState().value