نظارت بر داده ها در پس زمینه

به‌روزرسانی‌های غیرفعال داده برای برنامه‌هایی مناسب است که باید داده‌های خدمات بهداشتی را در پس‌زمینه نظارت کنند. آنها برای موارد استفاده ای در نظر گرفته شده اند که ساعت ها، روزها یا حتی بیشتر طول می کشد. اگر زمانی که برنامه شما اجرا نمی شود و کاربر به طور صریح درگیر تمرینی نیست، نیاز به ذخیره یا پردازش داده های سلامت دارید، از سرویس گیرنده غیرفعال Health Service استفاده کنید.

برای مثال‌هایی از استفاده از داده‌های غیرفعال، نمونه‌های Passive Data و Passive Goals را در GitHub ببینید.

وابستگی ها را اضافه کنید

برای افزودن وابستگی به خدمات سلامت، باید مخزن Google Maven را به پروژه خود اضافه کنید. برای اطلاعات بیشتر، به مخزن Maven Google مراجعه کنید.

در فایل build.gradle در سطح ماژول، وابستگی زیر را اضافه کنید:

شیار

dependencies {
    implementation "androidx.health:health-services-client:1.1.0-alpha03"
}

کاتلین

dependencies {
    implementation("androidx.health:health-services-client:1.1.0-alpha03")
}

بررسی قابلیت ها

قبل از ثبت نام برای به‌روزرسانی داده، بررسی کنید که دستگاه می‌تواند نوع داده‌های مورد نیاز برنامه شما را ارائه دهد. بررسی قابلیت‌ها به شما امکان می‌دهد برخی ویژگی‌ها را فعال یا غیرفعال کنید یا رابط کاربری برنامه خود را برای جبران ویژگی‌هایی که در دسترس نیستند تغییر دهید.

val healthClient = HealthServices.getClient(this /*context*/)
val passiveMonitoringClient = healthClient.passiveMonitoringClient
lifecycleScope.launchWhenCreated {
    val capabilities = passiveMonitoringClient.capabilities.await()
    // Supported types for passive data collection
    supportsHeartRate =
        DataType.HEART_RATE_BPM in capabilities.supportedDataTypesPassiveMonitoring
    // Supported types for PassiveGoals
    supportsStepsGoal =
        DataType.STEPS_DAILY in capabilities.supportedDataTypesPassiveGoals
}

برای داده های غیرفعال ثبت نام کنید

شما می توانید داده های غیرفعال را از طریق یک سرویس، یک تماس یا هر دو دریافت کنید. زمانی که هیچ بخشی از برنامه شما در پیش زمینه قابل مشاهده نیست، یک سرویس به برنامه شما امکان می دهد داده ها را در پس زمینه دریافت کند. وقتی داده‌ها را در پس‌زمینه دریافت می‌کنید، به صورت دسته‌ای تحویل داده می‌شوند. برگشت به تماس داده ها را با سرعت کمی بیشتر دریافت می کند، اما فقط زمانی که برنامه در حال اجرا است و پاسخ تماس با موفقیت اطلاع داده می شود.

از هر روشی که استفاده می کنید، ابتدا یک PassiveListenerConfig ایجاد کنید که تعیین می کند کدام نوع داده را دریافت کنید، همانطور که در مثال زیر نشان داده شده است:

val passiveListenerConfig = PassiveListenerConfig.builder()
    .setDataTypes(setOf(DataType.HEART_RATE_BPM))
    .build()

برای دریافت داده‌ها با استفاده از پاسخ به تماس، پاسخ تماس را تعریف و ثبت کنید، همانطور که در مثال زیر نشان داده شده است:

val passiveListenerCallback: PassiveListenerCallback = object : PassiveListenerCallback {
    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
        // TODO: Do something with dataPoints
    }
}

passiveMonitoringClient.setPassiveListenerCallback(
    passiveListenerConfig,
    passiveListenerCallback
)

// To remove the listener
passiveMonitoringClient.clearPassiveListenerCallbackAsync()

استفاده از یک سرویس مشابه است، اما به جای ایجاد یک کلاس مشتق شده از PassiveListenerCallback ، از PassiveListenerService استخراج کنید، همانطور که در مثال زیر نشان داده شده است:

class PassiveDataService : PassiveListenerService() {
    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
        // TODO: Do something with dataPoints
    }
}

passiveMonitoringClient.setPassiveListenerServiceAsync(
    PassiveDataService::class.java,
    passiveListenerConfig
)

سپس، سرویس را در فایل AndroidManifest.xml خود اعلام کنید. نیاز به مجوز خدمات بهداشتی، که تضمین می کند فقط خدمات بهداشتی قادر به اتصال به این خدمات هستند:

<service android:name=".PassiveDataService"
    android:permission="com.google.android.wearable.healthservices.permission.PASSIVE_DATA_BINDING"
    android:exported="true" />

زمان را تفسیر کنید

داده‌هایی که از خدمات سلامت دریافت می‌کنید به صورت دسته‌ای هستند، بنابراین ممکن است نقاط داده از انواع مختلف یا چندین نقطه داده از یک نوع را در یک دسته دریافت کنید. برای تعیین ترتیب صحیح رویدادها، از مهرهای زمانی موجود در این اشیا به جای زمان دریافت آنها توسط برنامه خود استفاده کنید.

همانطور که در مثال زیر نشان داده شده است، ابتدا مهر زمانی را برای هر DataPoint محاسبه کنید:

val bootInstant =
    Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())

سپس این مقدار می تواند به getStartInstant() یا getEndInstant() ارسال شود.

پس از بوت شدن، ثبت نام ها را بازیابی کنید

ثبت داده‌های غیرفعال در طول راه‌اندازی مجدد ادامه نمی‌یابد. برای دریافت داده‌ها پس از راه‌اندازی مجدد دستگاه، ثبت‌های خود را با استفاده از یک BroadcastReceiver که به پخش سیستم ACTION_BOOT_COMPLETED گوش می‌دهد، دوباره ایجاد کنید.

در گیرنده، سعی نکنید ثبت نام ها را مستقیماً بازیابی کنید. در عوض، این قابلیت را به کارگر WorkManager واگذار کنید. هنگامی که دستگاه در حال راه اندازی است، خدمات بهداشتی ممکن است 10 ثانیه یا بیشتر طول بکشد تا درخواست ثبت داده غیرفعال را تأیید کند و این ممکن است از زمان اجرای مجاز یک BroadcastReceiver بیشتر شود. در مقابل، کارگران WorkManager محدودیت اجرای 10 دقیقه ای دارند.

قطعه زیر نشان می دهد که یک BroadcastReceiver ممکن است چگونه باشد:

class StartupReceiver : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       if (intent.action != Intent.ACTION_BOOT_COMPLETED) return


       // TODO: Check permissions first
       WorkManager.getInstance(context).enqueue(
           OneTimeWorkRequestBuilder<RegisterForPassiveDataWorker>().build()
       )
   }
}

class RegisterForPassiveDataWorker(
   private val appContext: Context,
   workerParams: WorkerParameters
) : Worker(appContext, workerParams) {

   override fun doWork(): Result {
       runBlocking {
           HealthServices.getClient(appContext)
                .passiveMonitoringClient
                .setPassiveListenerCallback(...)
       }
       return Result.success()
   }
}

برای اینکه سیستم این کد را هنگام بوت شدن دستگاه اجرا کند، دو تغییر در فایل AndroidManifest.xml ایجاد کنید.

ابتدا، مجوز زیر را به عنوان فرزند <manifest> اضافه کنید:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

دوم، فیلتر هدف گیرنده زیر را به عنوان فرزند <application> اضافه کنید:

<receiver
    android:name=".StartupReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

وضعیت فعالیت

کلاینت غیرفعال همچنین می تواند اطلاعات سطح بالایی در مورد وضعیت کاربر ارائه دهد، مانند اینکه آیا کاربر خواب است یا خیر. برای دریافت این به‌روزرسانی‌ها، مراحل زیر را دنبال کنید:

  1. مجوز ACTIVITY_RECOGNITION را درخواست کنید.
  2. در سازنده PassiveListenerConfig setShouldUserActivityInfoBeRequested(true) فراخوانی کنید.

روش onUserActivityInfoReceived() را در callback یا سرویس خود لغو کنید و از UserActivityInfo برگشتی استفاده کنید، همانطور که در مثال زیر نشان داده شده است:

override fun onUserActivityInfoReceived(info: UserActivityInfo) {
    val stateChangeTime: Instant = info.stateChangeTime // may be in the past!
    val userActivityState: UserActivityState = info.userActivityState
    if (userActivityState == UserActivityState.USER_ACTIVITY_ASLEEP) {
        // ...
    }
}

اهداف منفعل

می‌توانید یک کلاینت غیرفعال را پیکربندی کنید تا در صورت رسیدن به اهداف غیرفعال به برنامه اطلاع دهد، مثلاً کاربر 10000 مرحله را در روز انجام دهد.

برای انجام این کار، همانطور که در مثال زیر نشان داده شده است، یک هدف ایجاد کنید:

val dailyStepsGoal by lazy {
    val condition = DataTypeCondition(
        dataType = DataType.STEPS_DAILY,
        threshold = 10_000, // Trigger every 10000 steps
        comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
    )
    PassiveGoal(condition)
}

همانطور که در مثال زیر نشان داده شده است، این هدف را به PassiveListenerConfig خود اضافه کنید:

val passiveListenerConfig = PassiveListenerConfig.builder()
    .setDailyGoals(setOf(dailyStepsGoal))
    .build()

روش onGoalCompleted() را در callback یا سرویس خود نادیده بگیرید و از PassiveGoal برگشتی استفاده کنید، همانطور که در مثال زیر نشان داده شده است:

override fun onGoalCompleted(goal: PassiveGoal) {
    when (goal.dataTypeCondition.dataType) {
        DataType.STEPS_DAILY -> {
            // ...
        }
    }
}
{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}