تطوير تجارب النوم باستخدام Health Connect

يتوافق هذا الدليل مع الإصدار 1.1.0 من Health Connect أو الإصدارات الأحدث.

إذا أردت إنشاء تجربة لتتبُّع النوم في تطبيقك، يمكنك استخدام Health Connect لتنفيذ إجراءات مثل:

  • كتابة بيانات جلسات النوم
  • كتابة بيانات مرحلة النوم
  • كتابة بيانات النوم، مثل معدّل نبضات القلب ونسبة الأكسجين في الدم ومعدّل التنفس
  • قراءة بيانات النوم من تطبيقات أخرى

يوضّح هذا الدليل كيفية إنشاء ميزات النوم هذه، ويشمل أنواع البيانات، والتنفيذ في الخلفية، والأذونات، وسير العمل المقترَح، وأفضل الممارسات.

نظرة عامة: إنشاء أداة شاملة لتتبُّع النوم

يمكنك إنشاء تجربة شاملة لتتبُّع النوم باستخدام Health Connect من خلال اتّباع الخطوات الأساسية التالية:

  • تنفيذ الأذونات بشكل صحيح استنادًا إلى "أذونات الصحة"
  • تسجيل الجلسات باستخدام SleepSessionRecord
  • كتابة أنواع البيانات، مثل مراحل النوم ومعدّل نبضات القلب ونسبة الأكسجين في الدم، بشكل متّسق أثناء الجلسة
  • إدارة التنفيذ في الخلفية بشكل صحيح للتحقّق من تسجيل البيانات بشكل مستمر طوال الليل
  • قراءة بيانات جلسات النوم لإنشاء ملخّص جودة ومدة النوم وتحليلات بعد النوم

يتيح سير العمل هذا إمكانية التشغيل التفاعلي مع تطبيقات Health Connect الأخرى ويتحقّق من إمكانية وصول المستخدم إلى البيانات.

قبل البدء

قبل تنفيذ ميزات النوم:

المفاهيم الرئيسية

يمثّل Health Connect بيانات النوم باستخدام بعض المكوّنات الأساسية. يُعدّ SleepSessionRecord السجلّ المركزي للنوم، ويحتوي على تفاصيل مثل أوقات البدء أو الانتهاء ومراحل النوم. أثناء الجلسة، يمكن تسجيل أنواع بيانات مختلفة، مثل HeartRateRecord أو OxygenSaturationRecord.

جلسات النوم

يتم تمثيل بيانات النوم باستخدام SleepSessionRecord. يخزّن كل سجلّ ما يلي:

  • startTime
  • endTime
  • ‫stages: قائمة بـ SleepSessionRecord.Stage، بما في ذلك النوم العميق والخفيف وحركة العين السريعة والاستيقاظ
  • البيانات الوصفية الاختيارية للجلسة (العنوان والملاحظات)

قد تكتب التطبيقات أنواع بيانات متعدّدة مرتبطة بجلسة معيّنة.

أنواع البيانات

تشمل أنواع البيانات الشائعة التي يتم تسجيلها خلال جلسة النوم ما يلي:

  • SleepSessionRecord: يسجّل مدة النوم ومراحله، بما في ذلك النوم العميق والخفيف ومرحلة حركة العين السريعة والاستيقاظ.
  • ‫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
            }
        }
    }
}

أفضل الممارسات

اتّبِع الإرشادات التالية لتحسين موثوقية البيانات وتجربة المستخدم:

  • كتابة البيانات بشكل متكرر أثناء التتبُّع النشط: بالنسبة إلى التتبُّع النشط، اكتب البيانات فور توفّرها أو بفاصل زمني أقصاه 15 دقيقة.
  • استخدام WorkManager لعمليات المزامنة في الخلفية: استخدِم WorkManager لعمليات الكتابة المؤجَّلة. ننصحك بضبط الفاصل الزمني على 15 دقيقة لتحقيق التوازن بين البيانات في الوقت الفعلي وكفاءة البطارية.
  • طلبات الكتابة المجمّعة: لا تكتب كل حدث من أحداث أداة الاستشعار بشكل فردي. تقسيم طلباتك إلى أجزاء يعالج تطبيق Health Connect ما يصل إلى 1000 سجلّ لكل طلب كتابة.
  • الحفاظ على ثبات معرّفات الجلسات وفرادتها: استخدِم معرّفات متّسقة لجلساتك. إذا تم تعديل جلسة أو تحديثها، فإنّ استخدام المعرّف نفسه يمنع التعامل معها كجلسة جديدة منفصلة.
  • استخدام ميزة تجميع أنواع البيانات: لتقليل عبء عمليات الإدخال/الإخراج والحفاظ على عمر البطارية، يمكنك تجميع نقاط البيانات في طلب 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 Toolbox: يمكنك استخدام هذا التطبيق المصاحب لفحص السجلات يدويًا وحذف بيانات الاختبار ومحاكاة التغييرات في قاعدة البيانات. إنها أفضل طريقة للتحقق من تخزين سجلاتك بشكل صحيح.
  • اختبار الوحدات باستخدام FakeHealthConnectClient: استخدِم مكتبة الاختبار للتحقّق من كيفية تعامل تطبيقك مع الحالات الاستثنائية، مثل إلغاء الأذونات أو استثناءات واجهة برمجة التطبيقات بدون الحاجة إلى جهاز فعلي.

قائمة التحقّق من الجودة

البنية النموذجية

يتضمّن تنفيذ ميزة تتبُّع النوم عادةً ما يلي:

المكوّن يدير
وحدة التحكّم في الجلسة حالة الجلسة
الموقّت
منطق تجميع البيانات
وحدات التحكّم في أنواع البيانات
جمع البيانات
طبقة المستودع (تتضمّن عمليات Health Connect:) إدراج جلسة
إدراج أنواع البيانات
إدراج مراحل النوم
قراءة ملخّصات الجلسات
طبقة واجهة المستخدم (الشاشات): المدة
أنواع البيانات المباشرة
عرض مراحل النوم

تحديد المشاكل وحلّها

المشكلة السبب المحتمل درجة الدقة
أنواع البيانات غير المتوفّرة (مثل معدّل نبضات القلب) عدم توفّر أذونات التعديل أو فلاتر الوقت غير الصحيحة تأكَّد من أنّك طلبت من المستخدم منحك الإذن بالوصول إلى نوع البيانات المحدّد وأنّه قد منحك هذا الإذن. تأكَّد من أنّ ReadRecordsRequest يستخدم TimeRangeFilter يتطابق مع الجلسة. يُرجى الاطّلاع على الأذونات.
تعذُّر كتابة الجلسة طوابع زمنية متداخلة قد يرفض Health Connect السجلّات التي تتداخل مع البيانات الحالية من التطبيق نفسه. يُرجى التأكّد من أنّ startTime الجلسة الجديدة يأتي بعد endTime الجلسة السابقة.
لم يتم تسجيل أي بيانات من أجهزة الاستشعار أثناء النوم تم إنهاء الخدمة التي تعمل في المقدّمة أو أنّها غير نشطة. لجمع بيانات أجهزة الاستشعار طوال الليل عندما تكون الشاشة مطفأة، يمكنك استخدام خدمة تعمل في المقدّمة مع foregroundServiceType="health".
ظهور سجلات مكررة السمة clientRecordId غير متوفّرة. خصِّصوا clientRecordId فريدًا في Metadata لكل سجلّ. يتيح ذلك لتطبيق Health Connect إزالة البيانات المكرّرة إذا تمت كتابة البيانات نفسها مرتين أثناء إعادة محاولة المزامنة. يُرجى الاطّلاع على أفضل الممارسات.

الخطوات الشائعة لتصحيح الأخطاء

تحقَّق من حالة الإذن. يجب دائمًا استدعاء getPermissionStatus() قبل محاولة إجراء عملية قراءة أو كتابة. ويمكن للمستخدمين إبطال الأذونات في إعدادات النظام في أي وقت.
تحقَّق من وضع التنفيذ. إذا كان تطبيقك لا يجمع البيانات في الخلفية، فتأكَّد من أنّك قد أعلنت عن الأذونات الصحيحة في ملف AndroidManifest.xml وأنّ المستخدم لم يضع التطبيق في وضع "تقييد استخدام البطارية".