توسعه «تجربه‌های خواب» با Health Connect

اگر می‌خواهید تجربه ردیابی خواب را در برنامه‌تان بسازید، می‌توانید از Health Connect برای انجام کارهایی مثل این‌ها استفاده کنید:

  • نوشتن داده‌های جلسه خواب
  • نوشتن داده‌های مرحله خواب
  • نوشتن داده‌های خواب مثل ضربان قلب، اشباع اکسیژن، و سرعت تنفس
  • خواندن داده‌های خواب از برنامه‌های دیگر

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

نمای کلی: ساختن ردیاب خواب جامع

با دنبال کردن این مراحل اصلی می‌توانید تجربه جامعی از ردیابی خواب بااستفاده از Health Connect بسازید:

  • اجرای صحیح اجازه‌ها براساس «اجازه‌های سلامت».
  • درحال ضبط جلسه‌ها بااستفاده از SleepSessionRecord.
  • نوشتن انواع داده مثل مراحل خواب، ضربان قلب، و اشباع اکسیژن به‌طور مداوم درطول جلسه.
  • مدیریت صحیح اجرای پس‌زمینه برای درستی‌سنجی ضبط پیوسته داده‌ها درطول شب.
  • درحال خواندن داده‌های جلسه برای خلاصه‌ها و تجزیه‌وتحلیل‌های پس‌از خواب.

این گردش کار امکان تعامل‌پذیری با سایر برنامه‌های Health Connect را فراهم می‌کند و دسترسی به داده‌های تحت کنترل کاربر را تأیید می‌کند.

قبل‌از شروع

قبل‌از پیاده‌سازی ویژگی‌های خواب:

مفاهیم اصلی

‫Health Connect داده‌های خواب را بااستفاده از چند مؤلفه اصلی نشان می‌دهد. ‫A SleepSessionRecord به‌عنوان سابقه مرکزی خواب عمل می‌کند و جزئیاتی مثل زمان شروع یا پایان و مراحل خواب را دربرمی‌گیرد. درطول جلسه، انواع مختلفی از داده‌ها مثل HeartRateRecord یا OxygenSaturationRecord می‌تواند ضبط شود.

جلسه‌های خواب

داده‌های خواب با SleepSessionRecord نشان داده می‌شود. هر سابقه این موارد را ذخیره می‌کند:

  • startTime
  • endTime
  • stages: فهرستی از SleepSessionRecord.Stage شامل خواب عمیق، سبک، REM، و بیداری.
  • فراداده اختیاری جلسه (عنوان، یادداشت‌ها)

برنامه‌ها ممکن است چندین نوع داده مرتبط با جلسه را بنویسند.

انواع داده

انواع داده‌های رایج که درطول جلسه خواب ضبط می‌شود عبارت‌اند از:

  • SleepSessionRecord: مدت و مراحل خواب شامل خواب عمیق، سبک، REM، و بیداری را ضبط می‌کند.
  • HeartRateRecord: ضربان قلب را درطول خواب ضبط می‌کند.
  • OxygenSaturationRecord: اشباع اکسیژن (SpO2) را درطول خواب ثبت می‌کند.
  • RespiratoryRateRecord: سرعت تنفس را درطول خواب ضبط می‌کند.

هر نوع داده به‌عنوان یک سابقه جداگانه ذخیره می‌شود.

ملاحظات توسعه

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

اجرا در پس‌زمینه

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

  • سرویس‌های پیش‌زمینه‌ای برای جمع‌آوری داده‌ها
  • ‫WorkManager برای نوشتن یا همگام‌سازی به‌تعویق‌افتاده
  • استراتژی‌های دسته‌بندی برای نوشتن منظم سوابق داده‌های دانه‌بندی‌شده مثل ضربان قلب

با یکسان نگه داشتن شناسه جلسه در همه نوشتن‌ها، تداوم را حفظ کنید.

اجازه‌ها

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

دسترسی به خواب با اجازه‌های زیر محافظت می‌شود:

  • android.permission.health.READ_SLEEP
  • android.permission.health.WRITE_SLEEP

برای افزودن قابلیت خواب به برنامه‌تان، با درخواست اجازه‌های نوع داده SleepSession شروع کنید.

برای اینکه بتوانید وضعیت خواب را بنویسید، باید اجازه زیر را اعلام کنید:

<application>
  <uses-permission
android:name="android.permission.health.WRITE_SLEEP" />
...
</application>

برای خواندن خواب، باید اجازه‌های زیر را درخواست کنید:

<application>
  <uses-permission
android:name="android.permission.health.READ_SLEEP" />
...
</application>

در زیر نمونه‌ای از نحوه درخواست اجازه برای جلسه خواب که شامل داده‌های ضربان قلب، اشباع اکسیژن، و تعداد تنفس است نشان داده شده است:

پس‌از ایجاد نمونه کارخواه، برنامه شما باید از کاربر اجازه درخواست کند. کاربران باید بتوانند در هر زمانی اجازه‌ها را اعطا یا رد کنند. برای انجام این کار، مجموعه‌ای از اجازه‌ها را برای انواع داده موردنیاز ایجاد کنید. ابتدا مطمئن شوید که اجازه‌های موجود در مجموعه در مانیفست Android شما اعلام شده باشد.

val permissions =
    setOf(
        HealthPermission.getReadPermission(SleepSessionRecord::class),
        HealthPermission.getWritePermission(SleepSessionRecord::class),
        HealthPermission.getReadPermission(HeartRateRecord::class),
        HealthPermission.getWritePermission(HeartRateRecord::class),
        HealthPermission.getReadPermission(OxygenSaturationRecord::class),
        HealthPermission.getWritePermission(OxygenSaturationRecord::class),
        HealthPermission.getReadPermission(RespiratoryRateRecord::class),
        HealthPermission.getWritePermission(RespiratoryRateRecord::class)
    )
از getGrantedPermissions استفاده کنید تا ببینید آیا برنامه شما ازقبل اجازه‌های لازم را دارد یا نه. درغیراین‌صورت، از createRequestPermissionResultContract برای درخواست این اجازه‌ها استفاده کنید. با این کار، صفحه اجازه‌های Health Connect نمایش داده می‌شود.
val permissions = setOf(
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class),
        HealthPermission.getReadPermission(HeartRateRecord::class),
        HealthPermission.getWritePermission(HeartRateRecord::class)
    )

val requestPermissionsLauncher = rememberLauncherForActivityResult(
    contract = PermissionController.createRequestPermissionResultContract()
) { grantedPermissions ->
    if (grantedPermissions.containsAll(permissions)) {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") }
    } else {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") }
    }
}
ازآنجایی‌که کاربران می‌توانند در هر زمانی اجازه‌ها را اعطا یا لغو کنند، برنامه شما باید هر بار قبل‌از استفاده از اجازه‌ها، آن‌ها را بررسی کند و سناریوهایی را که اجازه ازدست می‌رود مدیریت کند.

پیاده‌سازی جلسه خواب

این بخش گردش کار توصیه‌شده برای ضبط داده‌های خواب را شرح می‌دهد.

برای هم‌راستا کردن انواع داده مثل HeartRateRecord یا OxygenSaturationRecord با جلسه خواب، آن‌ها را با مُهرهای زمانی که بین startTime و endTime جلسه قرار دارند ضبط کنید. ‫Health Connect از شناسه جلسه برای پیوند دادن جلسه‌های خواب با داده‌های دقیق استفاده نمی‌کند. درعوض، ارتباط ازطریق فاصله‌های زمانی هم‌پوشان ضمنی است. هنگام خواندن داده‌های خواب، می‌توانید از محدوده زمانی جلسه برای پُرسمان کردن انواع داده‌های مرتبط استفاده کنید، همان‌طور که در خواندن داده‌های خواب نشان داده شده است.

نوشتن جلسه

درحالی‌که داده‌های دقیق مثل ضربان قلب را می‌توان درطول جلسه خواب ثبت کرد، خود SleepSessionRecord فقط باید پس‌از پایان جلسه در Health Connect نوشته شود، برای مثال وقتی کاربر بیدار می‌شود. این سابقه باید شامل جلسه startTime، endTime، و فهرستی از SleepSessionRecord.Stage اشیاء ضبط‌شده درطول جلسه باشد، زیرا SleepSessionRecord لازم دارد endTime بعداز startTime باشد.

برای نوشتن جلسه خواب:

  1. شناسه یکتای سابقه مشتری تولید کنید.
  2. وقتی کاربر بیدار می‌شود یا ردیابی خواب متوقف می‌شود، همه مراحل خواب را جمع‌آوری کنید و SleepSessionRecord را بسازید.
  3. بااستفاده از insertRecords، گزارش را درج کنید.

مثال:

val clientRecordId = UUID.randomUUID().toString()
val sessionStartTime = LocalDateTime.of(2023, 10, 30, 22, 0).toInstant(ZoneOffset.UTC)
val sessionEndTime = LocalDateTime.of(2023, 10, 31, 7, 0).toInstant(ZoneOffset.UTC)

val stages = mutableListOf<SleepSessionRecord.Stage>()
// Add recorded stages, for example:
stages.add(SleepSessionRecord.Stage(
    startTime = sessionStartTime.plusSeconds(3600),
    endTime = sessionStartTime.plusSeconds(7200),
    stage = SleepSessionRecord.STAGE_TYPE_LIGHT)
)
stages.add(SleepSessionRecord.Stage(
    startTime = sessionStartTime.plusSeconds(7200),
    endTime = sessionStartTime.plusSeconds(10800),
    stage = SleepSessionRecord.STAGE_TYPE_DEEP)
)
// ... other stages

val session = SleepSessionRecord(
    startTime = sessionStartTime,
    startZoneOffset = ZoneOffset.UTC,
    endTime = sessionEndTime,
    endZoneOffset = ZoneOffset.UTC,
    stages = stages,
    metadata = Metadata(clientRecordId = clientRecordId)
)

healthConnectClient.insertRecords(listOf(session))

درحال خواندن داده‌های خواب

برنامه‌ها می‌توانند جلسات خواب و داده‌های مرتبط با آن را برای خلاصه کردن فعالیت، ارائه اطلاعات آماری سلامتی، یا همگام‌سازی داده‌ها با سرور خارجی بخوانند. برای مثال، می‌توانید SleepSessionRecord را بخوانید و سپس HeartRateRecord را که در همان بازه زمانی رخ داده است پُرسمان کنید.

خواندن جلسه با داده‌های مرتبط

می‌توانید جلسه‌های خواب را بااستفاده از ReadRecordsRequest با SleepSessionRecord به‌عنوان نوع گزارش، فیلترشده براساس محدوده زمانی، بخوانید. برای خواندن داده‌های مرتبط با جلسه معینی، درخواست دومی برای نوع داده انتخاب‌شده، مثل HeartRateRecord، ارائه دهید و براساس startTime و endTime جلسه خواب فیلتر کنید.

مثال زیر نشان می‌دهد که چگونه جلسات خواب را با داده‌های ضربان قلب منسوب به آن برای یک محدوده زمانی معین بخوانیم:

suspend fun readSleepSessionsWithAssociatedData(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    val response = healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = SleepSessionRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )

    for (sleepRecord in response.records) {
        // Process each session
        val stages = sleepRecord.stages
        val notes = sleepRecord.notes

        // To read specific granular data (like heart rate) that occurred during
        // this session, use the session's startTime and endTime to filter
        // the request for that data type.
        val hrResponse = healthConnectClient.readRecords(
            ReadRecordsRequest(
                recordType = HeartRateRecord::class,
                timeRangeFilter = TimeRangeFilter.between(
                    sleepRecord.startTime,
                    sleepRecord.endTime
                )
            )
        )
        for (heartRateRecord in hrResponse.records) {
            for (sample in heartRateRecord.samples) {
                val bpm = sample.beatsPerMinute
            }
        }
    }
}

روال‌های مطلوب

برای بهبود قابلیت اطمینان داده‌ها و تجربه کاربری، این رهنمودها را دنبال کنید:

  • نوشتن مکرر درطول ردیابی فعال: برای ردیابی فعال، داده‌ها را به‌محض دردسترس قرار گرفتن یا در حداکثر فاصله زمانی ۱۵ دقیقه بنویسید.
  • استفاده از WorkManager برای همگام‌سازی‌های پس‌زمینه: از WorkManager برای نوشتن‌های به‌تعویق‌افتاده استفاده کنید. برای ایجاد تعادل بین داده‌های هم‌زمان و کارایی باتری، فاصله زمانی ۱۵ دقیقه‌ای را هدف قرار دهید.
  • درخواست‌های نوشتن دسته‌ای: هر رویداد حسگر را به‌صورت جداگانه ننویسید. درخواست‌هایتان را دسته‌بندی کنید. ‫Health Connect در هر درخواست نوشتن حداکثر ۱۰۰۰ سابقه را مدیریت می‌کند.
  • شناسه‌های جلسه را ثابت و یکتا نگه دارید: از شناسه‌های یکسان برای جلسات خود استفاده کنید. اگر جلسه‌ای ویرایش یا به‌روز شود، استفاده از همان شناسه باعث می‌شود که به‌عنوان جلسه جدید و جداگانه درنظر گرفته نشود.
  • استفاده از دسته‌بندی برای انواع داده: برای کاهش سربار ورودی/خروجی و حفظ عمر باتری، نقطه‌های داده را به‌جای اینکه هر نقطه را به‌صورت جداگانه بنویسید، در یک فراخوانی insertRecords گروه‌بندی کنید.
  • از نوشتن داده‌های تکراری خودداری کنید: از «شناسه‌های مشتری» استفاده کنید: هنگام ایجاد سوابق، metadata.clientRecordId را تنظیم کنید. ‫Health Connect از این برای شناسایی سوابق منحصربه‌فرد استفاده می‌کند. اگر سعی کنید گزارشی با clientRecordId که ازقبل وجود دارد بنویسید، Health Connect از تکراری بودن آن چشم‌پوشی می‌کند یا گزارش موجود را به‌روز می‌کند و گزارش جدیدی ایجاد نمی‌کند. تنظیم metadata.clientRecordId مؤثرترین راه برای جلوگیری از موارد تکراری درطول تلاش‌های مجدد همگام‌سازی یا بازنصب برنامه است.
    val record = StepsRecord(
        count = 100,
        startTime = startTime,
        endTime = endTime,
        startZoneOffset = ZoneOffset.UTC,
        endZoneOffset = ZoneOffset.UTC,
        metadata = Metadata(
            // Use a unique ID from your own database
            clientRecordId = "daily_steps_2023_10_27_user_123"
        )
    )
  • بررسی داده‌های موجود: قبل‌از همگام‌سازی، محدوده زمانی را پُرسمان کنید تا ببینید آیا سوابق برنامه شما ازقبل وجود دارد یا نه.
  • مطمئن شوید مُهرهای زمان هم‌پوشانی نداشته باشند: بررسی کنید که جلسه جدید قبل‌از پایان جلسه قبلی شروع نشود. جلسه‌های هم‌پوشانی می‌توانند باعث ایجاد تعارض در داشبوردهای تناسب اندام و محاسبات خلاصه شوند.
  • دلیل‌های واضح برای اجازه ارائه دهید: از جریان Permission.createIntent برای توضیح اینکه چرا برنامه‌تان به داده‌های سلامتی نیاز دارد استفاده کنید، برای مثال: «برای نظارت بر روند فشار خون شما و ارائه اطلاعات آماری.»
  • آزمایش جلسه‌های طولانی‌مدت: مصرف باتری را در جلسه‌هایی که چند ساعت طول می‌کشند پایش کنید تا مطمئن شوید که فاصله دسته‌ای و استفاده از حسگر باعث خالی شدن شارژ دستگاه نمی‌شود.
  • تراز کردن مُهرهای زمان با نرخ‌های حسگر: مُهرهای زمان گزارش را با بسامد واقعی حسگرها مطابقت دهید تا داده‌ها با دقت بالا حفظ شوند.

آزمایش

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

ابزارهای درستی‌سنجی

  • جعبه‌ابزار Health Connect: از این برنامه همراه برای بازرسی دستی سوابق، حذف داده‌های آزمایشی، و شبیه‌سازی تغییرات در پایگاه داده استفاده کنید. این بهترین راه برای تأیید این است که سوابق شما به‌درستی ذخیره می‌شوند.
  • آزمایش واحد با FakeHealthConnectClient: از کتابخانه آزمایش استفاده کنید تا بدون نیاز به دستگاه فیزیکی، نحوه مدیریت موارد حاشیه‌ای مانند لغو اجازه یا استثناهای API را در برنامه‌تان درستی‌سنجی کنید.

بازبینه کیفیت

معماری معمول

پیاده‌سازی ردیابی خواب معمولاً شامل موارد زیر است:

مؤلفه مدیریت می‌کند
کنترل‌کننده جلسه وضعیت جلسه
زمان‌سنج
منطق دسته‌ای
کنترل‌کننده‌های انواع داده
جمع‌آوری داده
لایه مخزن (عملکردهای Health Connect را می‌پیچد:) درج جلسه
درج انواع داده
درج مراحل خواب
خواندن خلاصه جلسه
لایه میانای کاربر (نمایشگرها): مدت
انواع داده‌های زنده
مصورسازی مرحله خواب

عیب‌یابی

نشانه علت احتمالی برطرف کردن مشکل
انواع داده‌های موجود نیست (برای مثال، ضربان قلب) اجازه‌های نوشتن وجود ندارد یا فیلترهای زمان نادرست است. بررسی کنید که اجازه نوع داده خاص را درخواست کرده باشید و کاربر آن را اعطا کرده باشد. تأیید کنید که ReadRecordsRequest شما از TimeRangeFilter مطابق با جلسه استفاده می‌کند. اجازه‌ها را ببینید.
جلسه نوشته نشد مُهرهای زمان هم‌پوشانی دارند. ‫Health Connect ممکن است سوابقی را که با داده‌های موجود از همان برنامه هم‌پوشانی دارند رد کند. تأیید کنید که startTime جلسه جدید بعداز endTime جلسه قبلی باشد.
داده‌های حسگر درطول خواب ضبط نشد سرویس پیش‌زمینه‌ای غیرفعال شده است یا متوقف شده است. برای جمع‌آوری داده‌های حسگر در طول شب درحالی‌که صفحه خاموش است، می‌توانید از سرویس پیش‌زمینه‌ای با foregroundServiceType="health" استفاده کنید.
سوابق تکراری نشان داده می‌شود ‫clientRecordId وجود ندارد. در Metadata هر سابقه، clientRecordId یکتایی اختصاص دهید. این کار به Health Connect اجازه می‌دهد اگر داده‌های یکسانی درطول تلاش مجدد برای همگام‌سازی دوبار نوشته شود، آن‌ها را حذف کند. روال‌های مطلوب را ببینید.

مراحل رایج اشکال‌زدایی

وضعیت اجازه را بررسی کنید. همیشه قبل‌از تلاش برای انجام عملیات خواندن یا نوشتن، getPermissionStatus() را فراخوانی کنید. کاربران می‌توانند هرزمان بخواهند اجازه‌ها را در تنظیمات سیستم لغو کنند.
حالت اجرا را درستی‌سنجی کنید. اگر برنامه‌تان در پس‌زمینه داده جمع‌آوری نمی‌کند، بررسی کنید که اجازه‌های صحیح را در فایل AndroidManifest.xml خود اعلام کرده باشید و کاربر برنامه را در حالت «باتری محدودشده» قرار نداده باشد.