使用感應器管理工具在行動應用程式中填入步數資料,詳情請參閱下文 指南。如要進一步瞭解如何設計及管理運動應用程式 UI, 提及 建構基本的健身應用程式。
開始使用
如要開始評估基本步數計數器
行動裝置,您必須在應用程式模組中加入依附元件
build.gradle
檔案。請確保您使用的是最新版的依附元件。
此外,如果您將應用程式的支援拓展至其他板型規格 (例如 Wear OS),
請新增這些板型規格所需的依附元件。
以下列舉幾個 UI 依附元件的範例。如需完整清單 請參閱這份 UI 元素指南。
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 程式碼研究室。
如要設定 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
存取 API - 在
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()
}
如要初始化可控制應用程式步驟存取權的內容供應器 應用程式啟動後,立即將下列元素加入 應用程式的資訊清單檔案:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />