ใช้ Sensor Manager เพื่อป้อนข้อมูลจำนวนก้าวในแอปบนอุปกรณ์เคลื่อนที่ตามที่อธิบายไว้ใน คู่มือนี้ ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีออกแบบและจัดการ UI ของแอปออกกำลังกายได้ที่สร้างแอปฟิตเนสพื้นฐาน
เริ่มต้นใช้งาน
หากต้องการเริ่มต้นใช้งานการวัดจำนวนก้าวจากเครื่องนับก้าวพื้นฐานจากอุปกรณ์เคลื่อนที่ คุณจะต้องเพิ่มทรัพยากร Dependency ลงในไฟล์ build.gradle
ของโมดูลแอป ตรวจสอบว่าคุณใช้เวอร์ชันล่าสุดของทรัพยากร Dependency
นอกจากนี้ เมื่อขยายการรองรับแอปไปยังอุปกรณ์รูปแบบอื่นๆ เช่น Wear OS
ให้เพิ่มทรัพยากร Dependency ที่อุปกรณ์รูปแบบเหล่านี้ต้องการ
ตัวอย่างการขึ้นต่อกันของ 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")
รับเซ็นเซอร์เครื่องนับก้าว
หลังจากที่ผู้ใช้ให้สิทธิ์การจดจำกิจกรรมที่จำเป็นแล้ว คุณจะเข้าถึงเซ็นเซอร์ตัวนับก้าวได้โดยทำดังนี้
- รับออบเจ็กต์
SensorManager
จากgetSystemService()
- รับเซ็นเซอร์นับก้าวจาก
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
เป็นคอมโพเนนต์ที่ทำงานเบื้องหลัง
เพื่อให้การดำเนินการเชื่อถือได้ ดูข้อมูลเพิ่มเติมได้ในCodelab ของ 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
อินเทอร์เฟซ - ใน
onCreate()
method ให้จัดคิว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 ที่ควบคุมการเข้าถึงฐานข้อมูลตัวนับก้าวของแอปทันทีเมื่อแอปเริ่มต้น ให้เพิ่มองค์ประกอบต่อไปนี้ลงในไฟล์ Manifest ของแอป
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />