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

این راهنما با Health Connect نسخه ۱.۱.۰ یا بالاتر سازگار است.

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

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

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

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

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

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

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

قبل‌از شروع

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

مفاهیم اصلی

Health Connect داده‌های خواب را بااستفاده از چند مؤلفه اصلی نشان می‌دهد. ‫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: از کتابخانه آزمایشی برای درستی‌سنجی نحوه مدیریت موارد حاشیه‌ای توسط برنامه‌تان، مانند لغو اجازه یا استثناهای میانای برنامه‌سازی کاربردی، بدون نیاز به دستگاه فیزیکی استفاده کنید.

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

معماری معمول

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

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

عیب‌یابی

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

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

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