使用传感器管理器在移动应用中填充步数数据,如下所述 指南。如需详细了解如何设计和管理锻炼应用界面, 参阅 构建基本健身应用。
使用入门
要开始测量基本计步器的步数,请开始使用
您需要将依赖项添加到您的应用模块
build.gradle
文件。确保使用最新版本的依赖项。
此外,当您将应用的支持扩展到其他类型的设备(例如 Wear OS)时,
添加这些外形规格所需的依赖项
以下是一些界面依赖项的示例。如需查看完整列表 请参阅此界面元素指南。
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material")
获取计步器传感器
在用户授予必要的运动状态识别权限后, 您可以访问计步器传感器:
- 从
getSystemService()
获取SensorManager
对象。 - 从
SensorManager
获取计步器传感器:
private val sensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val sensor: Sensor? by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) }
部分设备没有计步器传感器。您应检查传感器 如果设备没有该设备,就会显示一条错误消息:
if (sensor == null) {
Text(text = "Step counter sensor is not present on this device")
}
创建前台服务
在基本的健身应用中,您可能会有一个按钮 接收来自用户的开始和停止事件以跟踪步数。
请谨记与传感器相关的最佳实践。 具体而言,计步器传感器在测量步数时, 监听器。通过将传感器注册与前台相关联 服务,只要需要,传感器就会注册,传感器可以 应用未在前台运行时保持注册状态。
使用以下代码段在 onPause()
方法中取消注册传感器
您的前台服务:
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
分析事件数据
如需访问传感器数据,请实现 SensorEventListener
接口。注意事项
您应该将传感器注册与前台服务的
生命周期,在服务暂停或结束时取消注册传感器。通过
以下代码段展示了如何实现 SensorEventListener
接口
对于 Sensor.TYPE_STEP_COUNTER
:
private const val TAG = "STEP_COUNT_LISTENER"
context(Context)
class StepCounter {
private val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
private val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
suspend fun steps() = suspendCancellableCoroutine { continuation ->
Log.d(TAG, "Registering sensor listener... ")
val listener: SensorEventListener by lazy {
object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent?) {
if (event == null) return
val stepsSinceLastReboot = event.values[0].toLong()
Log.d(TAG, "Steps since last reboot: $stepsSinceLastReboot")
if (continuation.isActive) {
continuation.resume(stepsSinceLastReboot)
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
Log.d(TAG, "Accuracy changed to: $accuracy")
}
}
}
val supportedAndEnabled = sensorManager.registerListener(listener,
sensor, SensorManager.SENSOR_DELAY_UI)
Log.d(TAG, "Sensor listener registered: $supportedAndEnabled")
}
}
为传感器事件创建数据库
您的应用可能会显示一个屏幕,供用户查看其在一段时间内的步数。 如需在您的应用中提供此功能,请使用 Room 持久性库。
以下代码段会创建一个包含一组步数的表格 测量值,以及您的应用访问每个测量值的时间:
@Entity(tableName = "steps")
data class StepCount(
@ColumnInfo(name = "steps") val steps: Long,
@ColumnInfo(name = "created_at") val createdAt: String,
)
创建数据访问对象 (DAO) 读写数据:
@Dao
interface StepsDao {
@Query("SELECT * FROM steps")
suspend fun getAll(): List<StepCount>
@Query("SELECT * FROM steps WHERE created_at >= date(:startDateTime) " +
"AND created_at < date(:startDateTime, '+1 day')")
suspend fun loadAllStepsFromToday(startDateTime: String): Array<StepCount>
@Insert
suspend fun insertAll(vararg steps: StepCount)
@Delete
suspend fun delete(steps: StepCount)
}
如需实例化 DAO,请创建一个 RoomDatabase
对象:
@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun stepsDao(): StepsDao
}
将传感器数据存储在数据库中
ViewModel 使用新的 StepCounter 类,因此您可以尽快存储步骤 您可以这样理解:
viewModelScope.launch {
val stepsFromLastBoot = stepCounter.steps()
repository.storeSteps(stepsFromLastBoot)
}
repository
类如下所示:
class Repository(
private val stepsDao: StepsDao,
) {
suspend fun storeSteps(stepsSinceLastReboot: Long) = withContext(Dispatchers.IO) {
val stepCount = StepCount(
steps = stepsSinceLastReboot,
createdAt = Instant.now().toString()
)
Log.d(TAG, "Storing steps: $stepCount")
stepsDao.insertAll(stepCount)
}
suspend fun loadTodaySteps(): Long = withContext(Dispatchers.IO) {
printTheWholeStepsTable() // DEBUG
val todayAtMidnight = (LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT).toString())
val todayDataPoints = stepsDao.loadAllStepsFromToday(startDateTime = todayAtMidnight)
when {
todayDataPoints.isEmpty() -> 0
else -> {
val firstDataPointOfTheDay = todayDataPoints.first()
val latestDataPointSoFar = todayDataPoints.last()
val todaySteps = latestDataPointSoFar.steps - firstDataPointOfTheDay.steps
Log.d(TAG, "Today Steps: $todaySteps")
todaySteps
}
}
}
}
定期检索传感器数据
如果您使用前台服务,则无需配置 WorkManager
因为在应用主动跟踪用户的步数时
更新后的总步数应该会显示在应用中。
不过,如果您希望批量记录步数,可以使用 WorkManager
来
按特定的时间间隔测量步数,如每 15 分钟一次。
WorkManager
是执行背景的组件
以确保执行有保证执行如需了解详情,请参阅 WorkManager Codelab。
如需配置 Worker
对象以检索数据,请替换 doWork()
方法,如以下代码段所示:
private const val TAG = " StepCounterWorker"
@HiltWorker
class StepCounterWorker @AssistedInject constructor(
@Assisted appContext: Context,
@Assisted workerParams: WorkerParameters,
val repository: Repository,
val stepCounter: StepCounter
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(TAG, "Starting worker...")
val stepsSinceLastReboot = stepCounter.steps().first()
if (stepsSinceLastReboot == 0L) return Result.success()
Log.d(TAG, "Received steps from step sensor: $stepsSinceLastReboot")
repository.storeSteps(stepsSinceLastReboot)
Log.d(TAG, "Stopping worker...")
return Result.success()
}
}
如需将 WorkManager
设置为每 15 分钟存储一次当前步数,请执行以下操作:
以下:
- 扩展
Application
类以实现Configuration.Provider
界面。 - 在
onCreate()
方法中,将PeriodicWorkRequestBuilder
加入队列。
此过程显示在以下代码段中:
@HiltAndroidApp
@RequiresApi(Build.VERSION_CODES.S)
internal class PulseApplication : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory
override fun onCreate() {
super.onCreate()
val myWork = PeriodicWorkRequestBuilder<StepCounterWorker>(
15, TimeUnit.MINUTES).build()
WorkManager.getInstance(this)
.enqueueUniquePeriodicWork("MyUniqueWorkName",
ExistingPeriodicWorkPolicy.UPDATE, myWork)
}
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
}
初始化控制对应用步骤的访问权限的 content provider 计数器数据库,请将以下元素添加到 应用的清单文件:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />