הקלטת תרגיל עם ExerciseClient

שירותי Health מספקים תמיכה ברמה הגבוהה ביותר לאפליקציות אימון באמצעות ExerciseClient. באמצעות ExerciseClient, האפליקציה יכולה לקבוע מתי מתבצע אימון, להוסיף יעדי אימון ולקבל עדכונים על מצב האימון, על אירועי אימון או על מדדים אחרים. למידע נוסף, ראו את הרשימה המלאה של סוגי האימונים שנתמכים ב-Health Services.

הדוגמה לתרגול ב-GitHub.

הוספת יחסי תלות

כדי להוסיף תלות ב-Health Services, צריך להוסיף את מאגר Google Maven לפרויקט. למידע נוסף, ראו מאגר Maven של Google.

לאחר מכן, מוסיפים את התלות הבאה לקובץ build.gradle ברמת המודול:

Groovy

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

Kotlin

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

מבנה האפליקציה

כשאתם יוצרים אפליקציית אימונים באמצעות Health Services, כדאי להשתמש במבנה האפליקציה הבא:

  • כדאי לשמור את המסכים והניווט בפעילות הראשית.
  • ניהול מצב האימון, נתוני החיישנים, הפעילות המתמשכת ונתונים באמצעות שירות בחזית.
  • אחסון נתונים באמצעות Room ושימוש ב-WorkManager כדי להעלות נתונים.

בזמן ההכנה לאימון ובמהלך האימון, יכול להיות שהפעילות שלכם תופסק מסיבות שונות. המשתמש עשוי לעבור לאפליקציה אחרת או לחזור לתצוגת השעון. יכול להיות שהמערכת תציג משהו מעל הפעילות שלכם, או שהמסך יכבה אחרי פרק זמן של חוסר פעילות. כדי לוודא שהאימון יתבצע בצורה תקינה, מומלץ להשתמש ב-ForegroundService שפועל באופן רציף בשילוב עם ExerciseClient.

שימוש ב-ForegroundService מאפשר להשתמש ב-Ongoing Activity API כדי להציג אינדיקטור במשטחי השעון, וכך לאפשר למשתמש לחזור במהירות לאימון.

חשוב לבקש את נתוני המיקום בצורה מתאימה בשירות שבחזית. בקובץ המניפסט, מציינים את סוגי השירותים שפועלים בחזית ואת ההרשאות הנדרשות:

<manifest ...>
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <application ...>
    
      <!-- If your app is designed only for devices that run Wear OS 4
           or lower, use android:foregroundServiceType="location" instead. -->
      <service
          android:name=".MyExerciseSessionRecorder"
          android:foregroundServiceType="health|location">
      </service>
      
    </application>
</manifest>

משתמשים ב-AmbientLifecycleObserver לפעילות שלפני האימון, שמכילה את הקריאה prepareExercise(), ולפעילות האימון. עם זאת, אל תעדכנו את המסך במהלך האימון במצב אווירה: הסיבה לכך היא ששירותי Health אוספים את נתוני האימון כאשר מסך המכשיר במצב אווירה כדי לחסוך בחשמל, ולכן יכול להיות שהמידע שמוצג לא יהיה עדכני. במהלך האימונים, כדאי להציג נתונים שיהיו הגיוניים למשתמש, כמו מידע עדכני או מסך ריק.

בדיקת היכולות

כל ExerciseType תומך בסוגים מסוימים של נתונים למדדים וליעדים של אימונים. כדאי לבדוק את היכולות האלה בזמן ההפעלה, כי הן עשויות להשתנות בהתאם למכשיר. יכול להיות שמכשיר מסוים לא תומך בסוג מסוים של אימון, או שהוא לא תומך בפונקציה ספציפית, כמו השהיה אוטומטית. בנוסף, יכול להיות שהיכולות של המכשיר ישתנו לאורך זמן, למשל אחרי עדכון תוכנה.

בזמן הפעלת האפליקציה, שולחים שאילתות לגבי יכולות המכשיר ומאחסנים ומעבדים את הנתונים הבאים:

  • התרגילים שתומכים בפלטפורמה.
  • התכונות שנתמכות בכל תרגול.
  • סוגי הנתונים הנתמכים בכל תרגול.
  • ההרשאות הנדרשות לכל אחד מסוגי הנתונים האלה.

אפשר להשתמש ב-ExerciseCapabilities.getExerciseTypeCapabilities() עם סוג האימון הרצוי כדי לראות אילו מדדים אפשר לבקש, אילו יעדי אימון אפשר להגדיר ואילו תכונות אחרות זמינות לסוג הזה. הדוגמה הבאה ממחישה זאת:

val healthClient = HealthServices.getClient(this /*context*/)
val exerciseClient = healthClient.exerciseClient
lifecycleScope.launch {
    val capabilities = exerciseClient.getCapabilitiesAsync().await()
    if (ExerciseType.RUNNING in capabilities.supportedExerciseTypes) {
        runningCapabilities =
            capabilities.getExerciseTypeCapabilities(ExerciseType.RUNNING)
    }
}

בתוך ExerciseTypeCapabilities המוחזר, הערך supportedDataTypes מציג את סוגי הנתונים שאפשר לבקש נתונים לגביהם. האפשרויות משתנות בהתאם למכשיר, לכן חשוב לא לבקש DataType שלא נתמך, אחרת הבקשה עשויה להיכשל.

משתמשים בשדות supportedGoals ו-supportedMilestones כדי לקבוע אם התרגיל יכול לתמוך ביעד פעילות גופנית שרוצים ליצור.

אם האפליקציה מאפשרת למשתמש להשתמש בהשהיה אוטומטית, צריך לבדוק אם המכשיר תומך בפונקציונליות הזו באמצעות supportsAutoPauseAndResume. ExerciseClient דוחה בקשות שלא נתמכות במכשיר.

בדוגמה הבאה בודקים את התמיכה בסוג הנתונים HEART_RATE_BPM, ביכולת של היעד STEPS_TOTAL ובפונקציונליות של ההשהיה האוטומטית:

// Whether we can request heart rate metrics.
supportsHeartRate = DataType.HEART_RATE_BPM in runningCapabilities.supportedDataTypes

// Whether we can make a one-time goal for aggregate steps.
val stepGoals = runningCapabilities.supportedGoals[DataType.STEPS_TOTAL]
supportsStepGoals = 
    (stepGoals != null && ComparisonType.GREATER_THAN_OR_EQUAL in stepGoals)

// Whether auto-pause is supported.
val supportsAutoPause = runningCapabilities.supportsAutoPauseAndResume

הרשמה לקבלת עדכונים על מצב התרגיל

עדכונים על תרגילים מועברים למאזין. האפליקציה יכולה לרשום רק מאזין אחד בכל פעם. מגדירים את המאזין לפני שמתחילים את האימון, כפי שמתואר בדוגמה הבאה. המאזין מקבל עדכונים רק על תרגילים שבבעלות האפליקציה.

val callback = object : ExerciseUpdateCallback {
    override fun onExerciseUpdateReceived(update: ExerciseUpdate) {
        val exerciseStateInfo = update.exerciseStateInfo
        val activeDuration = update.activeDurationCheckpoint
        val latestMetrics = update.latestMetrics
        val latestGoals = update.latestAchievedGoals
    }

    override fun onLapSummaryReceived(lapSummary: ExerciseLapSummary) {
        // For ExerciseTypes that support laps, this is called when a lap is marked.
    }

    override fun onAvailabilityChanged(
        dataType: DataType<*, *>,
        availability: Availability
    ) {
        // Called when the availability of a particular DataType changes.
        when {
            availability is LocationAvailability -> // Relates to Location/GPS.
            availability is DataTypeAvailability -> // Relates to another DataType.
        }
    }
}
exerciseClient.setUpdateCallback(callback)

ניהול משך החיים של האימון

שירותי Google Health תומכים בביצוע של אימון אחד בכל פעם בכל האפליקציות במכשיר. אם מתבצע מעקב אחר אימון ואפליקציה אחרת מתחילה לעקוב אחרי אימון חדש, האימון הראשון מסתיים.

לפני שמתחילים את האימון, מבצעים את הפעולות הבאות:

  • בודקים אם כבר מתבצע מעקב אחרי פעילות מסוימת, ומגיבים בהתאם. לדוגמה, אפשר לבקש מהמשתמש לאשר לפני שמבטלים אימון קודם ומתחילים לעקוב אחרי אימון חדש.

הדוגמה הבאה מראה איך לבדוק אם יש תרגול קיים באמצעות getCurrentExerciseInfoAsync:

lifecycleScope.launch {
    val exerciseInfo = exerciseClient.getCurrentExerciseInfoAsync().await()
    when (exerciseInfo.exerciseTrackedStatus) {
        OTHER_APP_IN_PROGRESS -> // Warn user before continuing, will stop the existing workout.
        OWNED_EXERCISE_IN_PROGRESS -> // This app has an existing workout.
        NO_EXERCISE_IN_PROGRESS -> // Start a fresh workout.
    }
}

הרשאות

כשמשתמשים ב-ExerciseClient, חשוב לוודא שהאפליקציה מבקשת את ההרשאות הנדרשות ושהן נשארות בתוקף. אם האפליקציה שלכם משתמשת בנתוני LOCATION, חשוב לוודא שהיא מבקשת את ההרשאות המתאימות לכך ושומרת עליהן.

בכל סוגי הנתונים, לפני שמפעילים את prepareExercise() או את startExercise(), צריך לבצע את הפעולות הבאות:

  • מציינים את ההרשאות המתאימות לסוגים המבוקשים של נתונים בקובץ AndroidManifest.xml.
  • מוודאים שהמשתמש העניק את ההרשאות הנדרשות. מידע נוסף זמין במאמר בקשה להרשאות לאפליקציה. אם ההרשאות הנדרשות לא ניתנו, הבקשה תידחה על ידי שירותי הבריאות.

לגבי נתוני מיקום, מבצעים את השלבים הנוספים הבאים:

הכנה לאימון

יכול להיות שיחלפו כמה שניות לפני שחיישנים מסוימים, כמו GPS או קצב הלב, יתחממו, או שהמשתמש ירצה לראות את הנתונים שלו לפני שהוא יתחיל את האימון. השיטה האופציונלית prepareExerciseAsync() מאפשרת לחימום החיישנים האלה ולקבלת נתונים בלי להפעיל את הטיימר של האימון. זמן ההכנה הזה לא משפיע על activeDuration.

לפני שמבצעים את הקריאה ל-prepareExerciseAsync(), צריך לבדוק את הדברים הבאים:

  • בודקים את הגדרת המיקום ברמת הפלטפורמה. המשתמש קובע את ההגדרה הזו בתפריט ההגדרות הראשי. היא שונה מבדיקת ההרשאות ברמת האפליקציה.

    אם ההגדרה מושבתת, צריך להודיע למשתמש שהוא דחה את הגישה למיקום, ולבקש ממנו להפעיל אותה אם האפליקציה שלכם דורשת גישה למיקום.

  • מוודאים שלאפליקציה יש הרשאות בסביבת זמן הריצה לחיישנים של הגוף (רמת API 35 ומטה) או לקצב הלב (רמת API 36 ואילך), לזיהוי פעילות ולמיקום מדויק. אם חסרות הרשאות, צריך לבקש מהמשתמש הרשאות זמן ריצה, תוך מתן הקשר מתאים. אם המשתמש לא מעניק הרשאה ספציפית, צריך להסיר את סוגי הנתונים המשויכים להרשאה הזו מהקריאה ל-prepareExerciseAsync(). אם לא ניתנו הרשאות למיקום או לחיישן הגוף (דופק ברמת API 36 ואילך), אל תפעילו את prepareExerciseAsync(), כי קריאת ההכנה מיועדת במיוחד לקבלת דופק יציב או תיקון GPS לפני תחילת האימון. האפליקציה עדיין יכולה לקבל את המרחק, הקצב, המהירות ומדדים אחרים שמבוססים על צעדים, ולא נדרשות לה הרשאות כאלה.

כדי לוודא שהקריאה ל-prepareExerciseAsync() תצליח, צריך לבצע את הפעולות הבאות:

  • משתמשים ב-AmbientLifecycleObserver לפעילות לפני האימון שמכילה את קריאת ההכנה.
  • קוראים ל-prepareExerciseAsync() מהשירות שפועל בחזית. אם הוא לא נמצא בשירות ומקושר למחזור החיים של הפעילות, יכול להיות שההכנה של החיישן תבוטל ללא צורך.
  • אם המשתמש עובר לפעילות אחרת לפני האימון, צריך להפעיל את endExercise() כדי לכבות את החיישנים ולהפחית את צריכת החשמל.

בדוגמה הבאה מוצגת קריאה ל-prepareExerciseAsync():

val warmUpConfig = WarmUpConfig(
    ExerciseType.RUNNING,
    setOf(
        DataType.HEART_RATE_BPM,
        DataType.LOCATION
    )
)
// Only necessary to call prepareExerciseAsync if body sensor (API level 35
// or lower), heart rate (API level 36+), or location permissions are given.
exerciseClient.prepareExerciseAsync(warmUpConfig).await()

// Data and availability updates are delivered to the registered listener.

אחרי שהאפליקציה נמצאת במצב PREPARING, עדכוני הזמינות של החיישנים מועברים ב-ExerciseUpdateCallback דרך onAvailabilityChanged(). לאחר מכן אפשר להציג את המידע הזה למשתמש כדי שהוא יוכל להחליט אם להתחיל את האימון.

מתחילים את האימון

כשרוצים להתחיל אימון, יוצרים ExerciseConfig כדי להגדיר את סוג האימון, סוגי הנתונים שרוצים לקבל מדדים לגביהם ואת יעדי האימון או אבני הדרך שלו.

יעדי אימון מורכבים מ-DataType ומתנאי. יעדי אימון הם יעד חד-פעמי שמופעל כשמתקיים תנאי מסוים, למשל כשהמשתמש רץ מרחק מסוים. אפשר גם להגדיר ציון דרך של אימון. אפשר להפעיל ציוני דרך של פעילות גופנית כמה פעמים, למשל בכל פעם שהמשתמש רץ לנקודה מסוימת מעבר למרחק שהוגדר.

בדוגמה הבאה מוסבר איך ליצור יעד אחד מכל סוג:

const val CALORIES_THRESHOLD = 250.0
const val DISTANCE_THRESHOLD = 1_000.0 // meters

suspend fun startExercise() {
    // Types for which we want to receive metrics.
    val dataTypes = setOf(
        DataType.HEART_RATE_BPM,
        DataType.CALORIES_TOTAL,
        DataType.DISTANCE
    )

    // Create a one-time goal.
    val calorieGoal = ExerciseGoal.createOneTimeGoal(
        DataTypeCondition(
            dataType = DataType.CALORIES_TOTAL,
            threshold = CALORIES_THRESHOLD,
            comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
        )
    )

    // Create a milestone goal. To make a milestone for every kilometer, set the initial
    // threshold to 1km and the period to 1km.
    val distanceGoal = ExerciseGoal.createMilestone(
        condition = DataTypeCondition(
            dataType = DataType.DISTANCE_TOTAL,
            threshold = DISTANCE_THRESHOLD,
            comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
        ),
        period = DISTANCE_THRESHOLD
    )

    val config = ExerciseConfig(
        exerciseType = ExerciseType.RUNNING,
        dataTypes = dataTypes,
        isAutoPauseAndResumeEnabled = false,
        isGpsEnabled = true,
        exerciseGoals = mutableListOf<ExerciseGoal<Double>>(calorieGoal, distanceGoal)
    )
    exerciseClient.startExerciseAsync(config).await()
}

אפשר גם לסמן הקפות בכל התרגילים. ב-Health Services מוצג ExerciseLapSummary עם מדדים שנצברו במהלך תקופת הנסיעה.

בדוגמה הקודמת מוצג השימוש ב-isGpsEnabled, שצריך להיות true כשמבקשים נתוני מיקום. עם זאת, השימוש ב-GPS יכול לעזור גם במדדים אחרים. אם ב-ExerciseConfig מצוין המרחק כ-DataType, ברירת המחדל היא להשתמש בצעדים כדי להעריך את המרחק. אם מפעילים את ה-GPS, אפשר להשתמש בנתוני המיקום כדי להעריך את המרחק.

השהיה, המשך והפסקה של אימון

אפשר להשהות, להמשיך ולסיים אימונים באמצעות הלחצן המתאים, למשל pauseExerciseAsync() או endExerciseAsync().

משתמשים במצב מ-ExerciseUpdate כמקור האמת. האימון נחשב להשהיה רק כשהמצב הזה משתקף בהודעה ExerciseUpdate, ולא כשהקריאה ל-pauseExerciseAsync() חוזרת. חשוב במיוחד להביא את הנושא הזה בחשבון כשמדובר במצבים של ממשק המשתמש. אם המשתמש לוחץ על 'השהיה', משביתים את לחצן ההשהיה ומפעילים את pauseExerciseAsync() בשירותי הבריאות. ממתינים עד ששירותי הבריאות מגיעים למצב מושהה באמצעות ExerciseUpdate.exerciseStateInfo.state, ואז מעבירים את הלחצן למצב 'המשך'. הסיבה לכך היא שזמן האספקה של עדכוני המצב של Health Services יכול להיות ארוך יותר מזמן הלחיצה על הלחצן, כך שאם תקשרו את כל השינויים בממשק המשתמש ללחיצות על לחצנים, יכול להיות שממשק המשתמש לא יתואם למצב של Health Services.

חשוב לזכור את זה במצבים הבאים:

  • השהיה אוטומטית מופעלת: האימון יכול להתחיל או להשהות ללא אינטראקציה של המשתמש.
  • אפליקציה אחרת מתחילה אימון: יכול להיות שהאימון יופסק ללא אינטראקציה של המשתמש.

אם האימון באפליקציה שלכם יופסק על ידי אפליקציה אחרת, האפליקציה שלכם צריכה לטפל בהפסקה בצורה נעימה:

  • שומרים את מצב האימון החלקי כדי שההתקדמות של המשתמש לא תימחק.
  • להסיר את סמל הפעילות המתמשכת ולשלוח למשתמש התראה על כך שהאימון שלו הסתיים על ידי אפליקציה אחרת.

בנוסף, צריך לטפל במקרה שבו ההרשאות בוטלו במהלך אימון מתמשך. הוא נשלח במצב isEnded, עם ExerciseEndReason של AUTO_END_PERMISSION_LOST. מטפלים במקרה הזה באופן דומה למקרה של סיום: שומרים את המצב החלקי, מסירים את הסמל של פעילות מתמשכת ושולחים התראה על מה שקרה למשתמש.

בדוגמה הבאה מוסבר איך לבדוק אם החשבון הושעה בצורה נכונה:

val callback = object : ExerciseUpdateCallback {
    override fun onExerciseUpdateReceived(update: ExerciseUpdate) {
        if (update.exerciseStateInfo.state.isEnded) {
            // Workout has either been ended by the user, or otherwise terminated
        }
        ...
    }
    ...
}

ניהול משך הפעילות הפעילה

במהלך אימון, האפליקציה יכולה להציג את משך הזמן הפעיל של האימון. האפליקציה, שירותי Health והיחידה הבקרת המיקרו (MCU) של המכשיר – המעבד בעל צריכת החשמל הנמוכה שאחראי למעקב אחר האימונים – צריכים להיות מסונכרנים, עם אותו משך זמן פעיל נוכחי. כדי לעזור בניהול הבעיה הזו, שירותי הבריאות שולחים ActiveDurationCheckpoint שמספק נקודת עיגון שממנה האפליקציה יכולה להפעיל את הטיימר שלה.

מכיוון שמשך הפעילות הפעילה נשלח מה-MCU ויכול להיות שיחלוף זמן מה עד שהוא יגיע לאפליקציה, השדה ActiveDurationCheckpoint מכיל שני מאפיינים:

  • activeDuration: משך הזמן שבו התרגיל היה פעיל
  • time: מתי נקבע משך הזמן הפעיל

לכן, באפליקציה אפשר לחשב את משך הפעילות של האימון לפי ActiveDurationCheckpoint באמצעות המשוואה הבאה:

(now() - checkpoint.time) + checkpoint.activeDuration

כך ניתן להסביר את ההפרש הקטן בין משך הפעילות שמחושב ב-MCU לבין משך הפעילות שמגיע לאפליקציה. אפשר להשתמש בנתונים האלה כדי להגדיר את הזמן בכרונומטר באפליקציה, וכך לוודא שהשעון של האפליקציה תואם לחלוטין לשעון ב-Health Services וב-MCU.

אם התרגיל מושהה, האפליקציה ממתינה להפעלה מחדש של הטיימר בממשק המשתמש עד שהזמן המחושב יחלוף מהזמן שמוצג כרגע בממשק המשתמש. הסיבה לכך היא שאות ההשהיה מגיע ל-Health Services ול-MCU עם עיכוב קל. לדוגמה, אם האפליקציה מושהית בשעה t=10 שניות, יכול להיות ש-Health Services לא יעביר את העדכון PAUSED לאפליקציה עד לשעה t=10.2 שניות.

עבודה עם נתונים מ-ExerciseClient

המדדים של סוגי הנתונים שהאפליקציה שלכם רשומה אליהם מועברים באמצעות הודעות ExerciseUpdate.

המעבד שולח הודעות רק כשהמכשיר פעיל או כשמגיעים לפרק הזמן המקסימלי לדיווח, למשל כל 150 שניות. אל תסתמכו על התדירות ExerciseUpdate כדי להעביר את הכרונומטר באמצעות activeDuration. בדוגמה הזו ב-GitHub מוסבר איך להטמיע שעון כרונומטר עצמאי.

כשמשתמש מתחיל אימון, אפשר לשלוח הודעות ExerciseUpdate בתדירות גבוהה, למשל בכל שנייה. כשהמשתמש מתחיל את האימון, יכול להיות שהמסך יכבה. לאחר מכן, שירותי Health יוכלו לשלוח נתונים בתדירות נמוכה יותר, אבל עדיין לדגום אותם באותה תדירות, כדי להימנע מהפעלה של המעבד הראשי. כשהמשתמש מביט במסך, כל הנתונים שנמצאים בתהליך קיבוץ נשלחים לאפליקציה באופן מיידי.

שליטה בקצב הקיבוץ

בתרחישים מסוימים, יכול להיות שתרצו לקבוע את התדירות שבה האפליקציה תקבל סוגי נתונים מסוימים כשהמסך כבוי. אובייקט BatchingMode מאפשר לאפליקציה לשנות את התנהגות ברירת המחדל של קיבוץ הנתונים כדי לקבל את הנתונים בתדירות גבוהה יותר.

כדי להגדיר את קצב האצווה:

  1. בודקים אם המכשיר תומך בהגדרה הספציפית של BatchingMode:

    // Confirm BatchingMode support to control heart rate stream to phone.
    suspend fun supportsHrWorkoutCompanionMode(): Boolean {
        val capabilities = exerciseClient.getCapabilities()
        return BatchingMode.HEART_RATE_5_SECONDS in
                capabilities.supportedBatchingModeOverrides
    }
    
  2. מציינים שאובייקט ExerciseConfig צריך להשתמש ב-BatchingMode מסוים, כפי שמתואר בקטע הקוד הבא.

    val config = ExerciseConfig(
        exerciseType = ExerciseType.WORKOUT,
        dataTypes = setOf(
            DataType.HEART_RATE_BPM,
            DataType.TOTAL_CALORIES
        ),
        // ...
        batchingModeOverrides = setOf(BatchingMode.HEART_RATE_5_SECONDS)
    )
    
  3. אפשר גם להגדיר את BatchingMode באופן דינמי במהלך האימון, במקום להשתמש בהתנהגות אצווה ספציפית לאורך כל האימון:

    val desiredModes = setOf(BatchingMode.HEART_RATE_5_SECONDS)
    exerciseClient.overrideBatchingModesForActiveExercise(desiredModes)
    
  4. כדי למחוק את BatchingMode בהתאמה אישית ולחזור להתנהגות ברירת המחדל, מעבירים קבוצה ריקה אל exerciseClient.overrideBatchingModesForActiveExercise().

חותמות זמן

נקודת הזמן של כל נקודה על הגרף מייצגת את משך הזמן מאז שהמכשיר הופעל. כדי להמיר את הערך הזה לחותמת זמן:

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

לאחר מכן אפשר להשתמש בערך הזה עם getStartInstant() או getEndInstant() לכל נקודת נתונים.

דיוק הנתונים

בחלק מסוגי הנתונים יכול להיות מידע על רמת הדיוק שמשויך לכל נקודה על הגרף. המאפיין הזה מיוצג במאפיין accuracy.

אפשר לאכלס את הכיתות HrAccuracy ו-LocationAccuracy עבור סוגי הנתונים HEART_RATE_BPM ו-LOCATION, בהתאמה. אם המאפיין accuracy קיים, אפשר להשתמש בו כדי לקבוע אם רמת הדיוק של כל נקודה בנתונים מספיקה לאפליקציה.

אחסון והעלאה של נתונים

שימוש ב-Room כדי לשמור נתונים שנשלחים מ-Health Services. העלאת הנתונים מתבצעת בסוף התרגיל באמצעות מנגנון כמו Work Manager. כך אפשר לוודא שהקריאות לרשת להעלאת נתונים יושהו עד לסיום התרגיל, וכך לצמצם את צריכת החשמל במהלך התרגיל ולפשט את העבודה.

רשימת בדיקה לאינטגרציה

לפני שמפרסמים אפליקציה שמשתמשת ב-ExerciseClient של שירותי הבריאות, כדאי לעיין ברשימת המשימות הבאה כדי לוודא שחוויית המשתמש לא תכלול בעיות נפוצות. ודאו ש:

  • האפליקציה בודקת את היכולות של סוג התרגיל ואת היכולות של המכשיר בכל פעם שהיא פועלת. כך תוכלו לזהות מתי מכשיר או תרגיל מסוימים לא תומכים באחד מסוגי הנתונים שהאפליקציה שלכם זקוקה להם.
  • אתם מבקשים את ההרשאות הנדרשות, שומרים עליהן ומציינים אותן בקובץ המניפסט. לפני שמפעילים את prepareExerciseAsync(), האפליקציה מאשרת שההרשאות בסביבת זמן הריצה הוענקו.
  • האפליקציה שלכם משתמשת ב-getCurrentExerciseInfoAsync() כדי לטפל במקרים שבהם:
    • כבר מתבצע מעקב אחרי אימון מסוים, והאפליקציה מבטלת את האימון הקודם.
    • אפליקציה אחרת סיימה את האימון. המצב הזה יכול לקרות כשהמשתמש פותח מחדש את האפליקציה, ומופיעה הודעה שמסבירה שהתרגיל הופסק כי אפליקציה אחרת לקחה את השליטה.
  • אם אתם משתמשים בנתונים של LOCATION:
    • האפליקציה שומרת ForegroundService עם foregroundServiceType התואם לכל משך התרגיל (כולל קריאת ההכנה).
    • בודקים אם ה-GPS מופעל במכשיר באמצעות isProviderEnabled(LocationManager.GPS_PROVIDER), ומבקשים מהמשתמש לפתוח את הגדרות המיקום במקרה הצורך.
    • בתרחישי שימוש תובעניים שבהם חשוב מאוד לקבל נתוני מיקום עם זמן אחזור קצר, מומלץ לשלב את ספק המיקום המשולב (FLP) ולהשתמש בנתונים שלו כנקודה ראשונית לקביעת המיקום. כשיש מידע יציב יותר על המיקום משירותי Health, משתמשים בו במקום ב-FLP.
  • אם האפליקציה שלכם דורשת העלאת נתונים, כל הקריאות לרשת להעלאת נתונים יושהו עד לסיום התרגיל. אחרת, במהלך התרגיל האפליקציה תבצע את הקריאות הנחוצות לרשת באופן חסכוני.