קריאת נתונים גולמיים

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

קריאת נתונים

‫Health Connect מאפשר לאפליקציות לקרוא נתונים ממאגר הנתונים כשהאפליקציה פועלת בחזית וברקע:

  • קריאות של נתונים כשהאפליקציה פועלת: בדרך כלל אפשר לקרוא נתונים מ-Health Connect כשהאפליקציה פועלת. במקרים כאלה, כדאי להשתמש בשירות פעיל כדי להריץ את הפעולה, למקרה שהמשתמש או המערכת ימקמו את האפליקציה ברקע במהלך פעולת קריאה.

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

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

כדי לקרוא רשומות, צריך ליצור ReadRecordsRequest ולספק אותו כשמתקשרים אל readRecords.

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

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.readRecords(
            ReadRecordsRequest(
                StepsRecord::class,
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        for (record in response.records) {
            // Process each record
        }
    } catch (e: Exception) {
        // Run error handling here
    }
}

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

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}

קריאה של נתוני הצעדים בנייד

ב-Android 14 (רמת API‏ 34) ובגרסה 20 ומעלה של SDK Extension,‏ Health Connect מספק ספירת צעדים במכשיר. אם אפליקציה כלשהי קיבלה את ההרשאה READ_STEPS, ‏ Health Connect מתחיל לתעד את הצעדים מהמכשיר עם Android, והמשתמשים רואים את נתוני הצעדים שנוספו אוטומטית לרשומות צעדים ב-Health Connect.

כדי לבדוק אם ספירת הצעדים במכשיר זמינה, צריך לוודא שבמכשיר פועלת מערכת Android 14 (רמת API‏ 34) ושיש בו לפחות SDK extension גרסה 20. אפשר להשתמש בקוד הבא:

val isStepTrackingAvailable =
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
        SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20

הצעדים בנייד שתועדו על ידי Health Connect מקבלים את הערך android ב-DataOrigin, שהוא שם החבילה. אם האפליקציה שלכם פשוט קוראת את מספר הצעדים המצטבר באמצעות aggregate ולא מסננת לפי DataOrigin, הצעדים שנמדדו במכשיר נכללים אוטומטית בסכום הכולל.

אם האפליקציה צריכה לקרוא את הנתונים של הצעדים שנמדדו במכשיר, או אם היא מציגה נתוני צעדים שמפורטים לפי אפליקציית המקור או המכשיר, אפשר לשלוח שאילתה לגבי רשומות שבהן הערך של DataOrigin הוא android. אם האפליקציה מציגה שיוך לנתוני שלבים, צריך לשייך נתונים מחבילת Android למכשיר הנוכחי. אפשר לעשות זאת באמצעות תווית כמו 'הטלפון שלך', אחזור שם המכשיר באמצעות Settings.Global.getString(resolver, Settings.Global.DEVICE_NAME) או בדיקת השדה Device במטא-נתונים של הרשומה.

בדוגמה הבאה אפשר לראות איך קוראים נתונים מצטברים של ספירת צעדים בנייד באמצעות סינון לפי android מקור הנתונים:

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = setOf(DataOrigin("android"))
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}

ספירת צעדים במכשיר

הסבר מפורט על התכונה 'ספירת צעדים במכשיר':

  • שימוש בחיישנים: אפליקציית Health Connect משתמשת בחיישן TYPE_STEP_COUNTER של SensorManager. החיישן הזה מותאם לצריכת חשמל נמוכה, ולכן הוא אידיאלי למעקב רציף אחרי צעדים ברקע.
  • גרנולריות הנתונים: כדי לחסוך בחיי הסוללה, נתוני הצעדים בדרך כלל נכתבים למסד הנתונים של Health Connect בקבוצות, ולא בתדירות גבוהה יותר מפעם בדקה.
  • שיוך: כמו שצוין קודם, כל השלבים שתועדו על ידי התכונה הזו במכשיר משויכים לשם החבילה android ב-DataOrigin.
  • הפעלה: מנגנון ספירת הצעדים במכשיר פעיל רק אם לפחות אפליקציה אחת במכשיר קיבלה את ההרשאה READ_STEPS ב-Health Connect.

דוגמה לקריאה ברקע

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

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

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

class ScheduleWorker(private val appContext: Context, workerParams: WorkerParameters):
    CoroutineWorker(appContext, workerParams) {

    override suspend fun doWork(): Result {
        // Read data and process it.
        ...

        // Return success indicating successful data retrieval
        return Result.success()
    }
}

if (healthConnectClient
    .features
    .getFeatureStatus(
    HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND
    ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {

    // Check if necessary permission is granted
    val grantedPermissions = healthConnectClient.permissionController.getGrantedPermissions()

    if (PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND !in grantedPermissions) {
        // Perform read in foreground
        ...
    } else {
        // Schedule the periodic work request in background
        val periodicWorkRequest = PeriodicWorkRequestBuilder<ScheduleWorker>(1, TimeUnit.HOURS)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "read_health_connect",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }
} else {
  // Background reading is not available, perform read in foreground
  ...
}

ערך ברירת המחדל של הפרמטר ReadRecordsRequest הוא pageSize (1,000). אם מספר הרשומות בבקשה אחת מסוג readResponse חורג מהערך pageSize של הבקשה, צריך לחזור על כל הדפים של התשובה כדי לאחזר את כל הרשומות באמצעות pageToken. עם זאת, חשוב להיזהר כדי לא לחרוג ממגבלות הקצב.

דוגמה לקריאה של pageToken

מומלץ להשתמש ב-pageToken לקריאת רשומות כדי לאחזר את כל הנתונים שזמינים מהתקופה המבוקשת.

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

val type = HeartRateRecord::class
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofDays(7))

try {
    var pageToken: String? = null
    do {
        val readResponse =
            healthConnectClient.readRecords(
                ReadRecordsRequest(
                    recordType = type,
                    timeRangeFilter = TimeRangeFilter.between(
                        startTime,
                        endTime
                    ),
                    pageToken = pageToken
                )
            )
        val records = readResponse.records
        // Do something with records
        pageToken = readResponse.pageToken
    } while (pageToken != null)
} catch (quotaError: IllegalStateException) {
    // Backoff
}

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

קריאת נתונים שנכתבו בעבר

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

יש הגבלות מסוימות על קריאה:

  • ב-Android מגרסה 14 ואילך

    • אין מגבלה היסטורית על אפליקציה שקוראת את הנתונים שלה.
    • מגבלה של 30 יום על אפליקציה שקוראת נתונים אחרים.
  • ב-Android מגרסה 13 ומטה

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

כדי להסיר את ההגבלות, צריך לבקש הרשאת קריאה.

כדי לקרוא נתונים היסטוריים, צריך לציין את שם החבילה כאובייקט DataOrigin בפרמטר dataOriginFilter של ReadRecordsRequest.

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

try {
    val response =  healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = HeartRateRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
            dataOriginFilter = setOf(DataOrigin("com.my.package.name"))
        )
    )
    for (record in response.records) {
        // Process each record
    }
} catch (e: Exception) {
    // Run error handling here
}

קריאת נתונים מלפני יותר מ-30 יום

כברירת מחדל, כל האפליקציות יכולות לקרוא נתונים מ-Health Connect עד 30 ימים לפני מועד מתן ההרשאה הראשונית.

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

היסטוריית ההרשאות של אפליקציה שנמחקה

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

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

טיפול בחריגים

אפליקציית Health Connect זורקת חריגים סטנדרטיים לפעולות CRUD כשנתקלים בבעיה. האפליקציה צריכה לזהות ולטפל בכל אחד מהחריגים האלה בצורה המתאימה.

בכל שיטה ב-HealthConnectClient מפורטים החריגים שיכולים להיות מושלכים. באופן כללי, האפליקציה צריכה לטפל בחריגים הבאים:

טבלה 1: חריגים ב-Health Connect ושיטות מומלצות
חריג תיאור שיטה מומלצת
IllegalStateException אחד מהתרחישים הבאים התרחש:

  • השירות Health Connect לא זמין.
  • הבקשה לא בנויה בצורה תקינה. לדוגמה, בקשה מצטברת בקטגוריות תקופתיות שבהן נעשה שימוש באובייקט Instant עבור timeRangeFilter.

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

  • גיבוי של כל קלט משתמש.
  • להיות מסוגלים לטפל בכל בעיה שמתרחשת במהלך פעולות כתיבה בכמות גדולה. לדוגמה, מוודאים שהתהליך מתקדם מעבר לבעיה ומבצעים את הפעולות שנותרו.
  • כדי לטפל בבעיות בבקשות, צריך להחיל אסטרטגיות של ניסיונות חוזרים והשהיה מעריכית (exponential backoff).

RemoteException אירעו שגיאות בשירות הבסיסי שאליו ה-SDK מתחבר, או בתקשורת איתו.

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

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

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