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

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

קריאת נתונים

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

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

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

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

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

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

val response = healthConnectClient.readRecords(
    ReadRecordsRequest(
        HeartRateRecord::class,
        timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
    )
)
response.records.forEach { record ->
    /* Process records */
}

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

suspend fun readStepsAggregate(startTime: Instant, endTime: Instant): Long {
    val response = healthConnectClient.aggregate(
        AggregateRequest(
            metrics = setOf(StepsRecord.COUNT_TOTAL),
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )
    return response[StepsRecord.COUNT_TOTAL] ?: 0L
}

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

ב-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

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

שינוי השיוך (Attribution) של שלבים במכשיר

החל מהעדכון של יוני 2026, הצעדים שמערכת Health Connect עוקבת אחריהם באופן מקורי משויכים לשם חבילה סינתטי (SPN), כמו com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e.

בעבר, שלבים מובנים שויכו לשם החבילה android. נתונים היסטוריים של שלבים שנרשמו לפני יוני 2026 ישמרו את android שם החבילה.

מספרי ה-SPN הם ספציפיים למכשיר ומוגבלים לכל אפליקציה כדי להגן על פרטיות המשתמשים:

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

שאילתה לגבי שלבים במכשיר

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

ספירת צעדים במכשיר דורשת גרסת SDK 20 ומעלה, אבל API‏ getCurrentDeviceDataSource() זמין ב-Android 14 (רמת API‏ 34) עם גרסת SDK 11 ומעלה.

ממשק ה-API של getCurrentDeviceDataSource() עדיין לא זמין בספריית Health Connect Jetpack. בדוגמאות הבאות נעשה שימוש ב-API של Android framework במקום זאת:

import android.content.Context
import android.health.connect.HealthConnectManager

val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
val deviceDataSource = healthConnectManager?.getCurrentDeviceDataSource()
val currentDeviceSpn = deviceDataSource?.deviceDataOrigin?.packageName

אם האפליקציה צריכה לקרוא את הנתונים של הצעדים שנמדדו במכשיר, או אם היא מציגה נתוני צעדים שמפורטים לפי אפליקציית המקור או המכשיר, צריך לשלוח שאילתה לגבי רשומות שבהן DataOrigin הוא android או תואם ל-SPN של המכשיר. אם האפליקציה מציגה שיוך לנתוני צעדים, צריך להשתמש ב-metadata.device כדי לזהות את מכשיר המקור של רשומות נפרדות. לשלבים במכשיר שמזוהים על ידי SPN בנתונים מצטברים, אפשר להשתמש במטא-נתונים של המכשיר, כמו model או manufacturer מ-DeviceDataSource, לצורך שיוך, או להשתמש בתווית כללית כמו 'הטלפון שלך' לשלבים במכשיר.

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

import android.content.Context
import android.health.connect.HealthConnectManager
import android.os.Build
import android.os.ext.SdkExtensions
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.records.metadata.DataOrigin
import androidx.health.connect.client.request.AggregateRequest
import androidx.health.connect.client.time.TimeRangeFilter
import java.time.Instant

suspend fun readDeviceStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    context: Context,
    startTime: Instant,
    endTime: Instant
) {
    // 1. Check if SDK Extension 11+ is available for getCurrentDeviceDataSource()
    val isDataSourceApiAvailable = Build.VERSION.SDK_INT >= Build.VERSION_CODES.U &&
            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.U) >= 11

    try {
        val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)

        // 2. Safely fetch the package name only if API is available and data exists
        val currentDeviceSpn = if (isDataSourceApiAvailable) {
            healthConnectManager?.getCurrentDeviceDataSource()?.deviceDataOrigin?.packageName
        } else {
            null
        }

        val dataOriginFilters = mutableSetOf(DataOrigin("android"))

        // 3. Explicit null-safety check using .let
        currentDeviceSpn?.let {
            dataOriginFilters.add(DataOrigin(it))
        }

        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = dataOriginFilters
            )
        )

        val stepCount = response[StepsRecord.COUNT_TOTAL]

    } catch (e: Exception) {
        // Now this catch block only handles actual runtime exceptions, 
        // rather than Errors from missing methods.
    }
}

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

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

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

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

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

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

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

    override suspend fun doWork(): Result {
        val healthConnectClient = HealthConnectClient.getOrCreate(applicationContext)
        // Perform background read logic here
        return Result.success()
    }
}
@OptIn(ExperimentalFeatureAvailabilityApi::class)
fun enqueueBackgroundReadWorker(context: Context, healthConnectClient: HealthConnectClient) {
    if (healthConnectClient
            .features
            .getFeatureStatus(
                HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND
            ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE
    ) {

        val periodicWorkRequest = PeriodicWorkRequestBuilder<ScheduleWorker>(1, TimeUnit.HOURS)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "read_health_connect",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }
}

ערך ברירת המחדל של הפרמטר 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 בקובץ המניפסט ובפעילות.