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

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

אפשר לעיין בדוגמה לתרגיל ב-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 Services מעבד את נתוני האימון בקבוצות כשהמסך של המכשיר במצב סביבה כדי לחסוך בחשמל, ולכן המידע שמוצג עשוי להיות לא עדכני. במהלך אימונים, הצגת נתונים שרלוונטיים למשתמש, או הצגת מידע עדכני או מסך ריק.

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

כל 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

הרשמה לעדכונים על מצב האימון

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

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)

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

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

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

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

בדוגמה הבאה אפשר לראות איך בודקים אם יש תרגיל קיים באמצעות 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.
  • מוודאים שהמשתמש העניק את ההרשאות הנדרשות. מידע נוסף זמין במאמר בנושא בקשת הרשאות לאפליקציה. אם ההרשאות הנדרשות לא ניתנו כבר, Health Services דוחה את הבקשה.

כדי להוסיף נתוני מיקום, מבצעים את השלבים הבאים:

הכנה לאימון

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

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

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

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

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

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

  • משתמשים ב-AmbientLifecycleObserver לפעילות שלפני האימון שכוללת את הקריאה prepare.
  • מתקשרים אל 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 כמקור האמת. האימון לא נחשב כמושהה כשהקריאה ל-pauseExerciseAsync() מוחזרת, אלא כשהמצב הזה משתקף בהודעה ExerciseUpdate. חשוב במיוחד לקחת את זה בחשבון כשמדובר במצבי ממשק משתמש. אם המשתמש לוחץ על ההשהיה, צריך להשבית את לחצן ההשהיה ולהתקשר אל pauseExerciseAsync() בשירותי הבריאות. מחכים עד שהשירותים בתחום הבריאות יגיעו למצב מושהה באמצעות ExerciseUpdate.exerciseStateInfo.state, ואז לוחצים על הכפתור כדי להפעיל אותם מחדש. הסיבה לכך היא שעדכוני סטטוס של שירותי הבריאות יכולים להימשך זמן רב יותר מאשר לחיצה על הלחצן, ולכן אם תקשרו את כל השינויים בממשק המשתמש ללחיצות על הלחצן, יכול להיות שהממשק לא יסתנכרן עם הסטטוס של שירותי הבריאות.

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

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

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

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

בנוסף, צריך לטפל במקרה שבו ההרשאות מבוטלות במהלך פעילות גופנית. הנתונים האלה נשלחים באמצעות המצב 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
        }
        ...
    }
    ...
}

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

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

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

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

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

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

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

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

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

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

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

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

שליטה בקצב העיבוד של קבוצות

במקרים מסוימים, יכול להיות שתרצו לשלוט בתדירות שבה האפליקציה מקבלת סוגים מסוימים של נתונים כשהמסך כבוי. אובייקט 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 Services, המערכת משתמשת בו במקום ב-FLP.
  • אם האפליקציה דורשת העלאת נתונים, כל קריאות הרשת להעלאת נתונים נדחות עד לסיום התרגיל. אחרת, במהלך התרגיל, האפליקציה תבצע את כל קריאות הרשת הנדרשות בצורה חסכונית.