Bir mobil cihazdaki adımları ölçmek için Sensör Yöneticisi'ni kullanın

Bu kılavuzda açıklandığı gibi, bir mobil uygulamadaki adım verilerini doldurmak için Sensör Yöneticisi'ni kullanın. Bir egzersiz uygulaması kullanıcı arayüzü tasarlama ve yönetme hakkında daha fazla bilgi için Temel bir fitness uygulaması oluşturma konusuna bakın.

Başlarken

Mobil cihazınızdan temel adım sayacınızın adımlarını ölçmeye başlamak için uygulama modülü build.gradle dosyanıza bağımlılıkları eklemeniz gerekir. Bağımlılıkların en son sürümlerini kullandığınızdan emin olun. Ayrıca, uygulamanızın desteğini Wear OS gibi diğer form faktörlerini içerecek şekilde genişletirken, bu form faktörlerinin gerektirdiği bağımlılıkları ekleyin.

Aşağıda, kullanıcı arayüzü bağımlılıklarından bazılarına birkaç örnek verilmiştir. Eksiksiz liste için bu Kullanıcı Arayüzü Öğeleri kılavuzuna bakın.

implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material")

Adım sayacı sensörünü al

Kullanıcı gerekli etkinlik tanıma iznini verdikten sonra, adım sayacı sensörüne erişebilirsiniz:

  1. getSystemService() öğesinden SensorManager nesnesini edinin.
  2. SensorManager öğesinden adım sayacı sensörünü alın:
private val sensorManager by lazy {
        getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val sensor: Sensor? by lazy {
        sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) }

Bazı cihazlarda adım sayacı sensörü yoktur. Sensör olup olmadığını kontrol etmeniz ve cihazda yoksa bir hata mesajı göstermeniz gerekir:

if (sensor == null) {
    Text(text = "Step counter sensor is not present on this device")
}

Ön plan hizmetinizi oluşturma

Temel bir fitness uygulamasında, adımları izlemek üzere kullanıcıdan başlatma ve durdurma etkinlikleri almayı sağlayan bir düğmeniz olabilir.

Sensör en iyi uygulamalarını göz önünde bulundurun. Özellikle, adım sayacı sensörü yalnızca sensör dinleyicisi kayıtlıyken adımları saymalıdır. Sensör kaydını bir ön plan hizmetiyle ilişkilendirerek sensör gerektiği sürece kayıtlı kalır ve uygulama ön planda değilken sensör kayıtlı kalabilir.

Ön plan hizmetinizin onPause() yöntemindeki sensörün kaydını iptal etmek için aşağıdaki snippet'i kullanın:

override fun onPause() {
    super.onPause()
    sensorManager.unregisterListener(this)
}

Etkinlik verilerini analiz etme

Sensör verilerine erişmek için SensorEventListener arayüzünü uygulayın. Sensör kaydını ön plan hizmetinizin kullanım ömrüyle ilişkilendirmeniz gerektiğini ve hizmet duraklatıldığında veya sona erdiğinde sensörün kaydını iptal etmeniz gerektiğini unutmayın. Aşağıdaki snippet, Sensor.TYPE_STEP_COUNTER için SensorEventListener arayüzünün nasıl uygulanacağını gösterir:

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")
    }
}

Sensör olayları için bir veritabanı oluşturun

Uygulamanızda, kullanıcının zaman içinde adımlarını görebileceği bir ekran gösterilebilir. Uygulamanızda bu özelliği sağlamak için Oda kalıcılığı kitaplığı'nı kullanın.

Aşağıdaki snippet, bir dizi adım sayısı ölçümünü ve uygulamanızın her bir ölçüme eriştiği süreyi içeren bir tablo oluşturur:

@Entity(tableName = "steps")
data class StepCount(
  @ColumnInfo(name = "steps") val steps: Long,
  @ColumnInfo(name = "created_at") val createdAt: String,
)

Verileri okumak ve yazmak için bir veri erişimi nesnesi (DAO) oluşturun:

@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'yu örneklendirmek için bir RoomDatabase nesnesi oluşturun:

@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun stepsDao(): StepsDao
}

Sensör verilerini veritabanında depolayın

ViewModel, yeni StepCounter sınıfını kullanır. Böylece adımları okur okumaz saklayabilirsiniz:

viewModelScope.launch {
    val stepsFromLastBoot = stepCounter.steps()
    repository.storeSteps(stepsFromLastBoot)
}

repository sınıfı şöyle görünür:

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
            }
        }
    }
}


Sensör verilerini düzenli olarak alma

Ön plan hizmeti kullanıyorsanız WorkManager uygulamasını yapılandırmanız gerekmez. Uygulamanızın kullanıcının adımlarını etkin bir şekilde izlediği süre boyunca, güncellenen toplam adım sayısı uygulamanızda görünmelidir.

Ancak adım kayıtlarınızı toplu hale getirmek istiyorsanız adımları belirli bir aralıkta (ör. 15 dakikada bir) ölçmek için WorkManager kullanabilirsiniz. WorkManager, garantili yürütme için arka plan işlemini gerçekleştiren bileşendir. WorkManager codelab'inden daha fazla bilgi edinin.

Worker nesnesini verileri alacak şekilde yapılandırmak için aşağıdaki kod snippet'inde gösterildiği gibi doWork() yöntemini geçersiz kılın:

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 işlevini, mevcut adım sayısını 15 dakikada bir kaydedecek şekilde ayarlamak için aşağıdakileri yapın:

  1. Configuration.Provider arayüzünü uygulamak için Application sınıfını genişletin.
  2. onCreate() yönteminde bir PeriodicWorkRequestBuilder öğesini sıraya alın.

Bu işlem aşağıdaki kod snippet'inde görünür:

@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()
}

Uygulama başladıktan hemen sonra uygulamanızın adım sayacı veritabanına erişimi kontrol eden içerik sağlayıcıyı başlatmak için aşağıdaki öğeyi uygulamanızın manifest dosyasına ekleyin:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove" />