خواندن داده‌های خام

مثال زیر نشان می‌دهد که چگونه داده‌های خام را به‌عنوان بخشی از گردش کار مشترک بخوانید.

خواندن داده‌ها

‫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) و «نسخه افزونه کیت توسعه نرم‌افزار» ۲۰ یا بالاتر، ‫Health Connect شمارش قدم‌ها در دستگاه را ارائه می‌دهد. اگر به برنامه‌ای اجازه READ_STEPS داده شده باشد، Health Connect شروع به ضبط تعداد قدم‌ها از دستگاه Android می‌کند و کاربران داده‌های تعداد قدم‌ها را به‌طور خودکار در ورودی‌های قدم‌ها در Health Connect می‌بینند.

برای بررسی اینکه آیا شمارش گام درون‌دستگاهی دردسترس است، مطمئن شوید دستگاه از Android 14 (سطح API 34) استفاده می‌کند و حداقل نسخه افزونه کیت توسعه نرم‌افزار آن ۲۰ است:

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

اگر برنامه شما تعداد گام‌های تجمیعی را بااستفاده از aggregate می‌خواند و براساس DataOrigin فیلتر نمی‌کند، گام‌های درون‌دستگاهی به‌طور خودکار در مجموع گنجانده می‌شود و برای به‌روزرسانی ژوئن ۲۰۲۶ نیازی به تغییر نیست.

تغییر اسنادی برای مراحل درون‌دستگاهی

از به‌روزرسانی ژوئن ۲۰۲۶، تعداد قدم‌هایی که Health Connect به‌صورت بومی ردیابی می‌کند به نام بسته مصنوعی (SPN)، مانند com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e، نسبت داده می‌شود.

قبلاً، مراحل داخلی به نام بسته android نسبت داده می‌شد. داده‌های گام سابقه که قبل‌از ژوئن ۲۰۲۶ ضبط شده است نام بسته android را حفظ می‌کند.

«نام‌های سرویس اصلی» مختص دستگاه هستند و براساس هر برنامه محدود می‌شوند تا از حریم خصوصی کاربر محافظت شود:

  • پایدار: SPN برای دستگاه فعلی برای برنامه شما پایدار است.
  • محدوده برنامه: برنامه‌های مختلف در یک دستگاه، «نام‌های اصلی سرویس» متفاوتی برای داده‌های گام درون‌دستگاهی می‌بینند.

پُرسمان برای مراحل درون‌دستگاهی

ازآنجایی‌که SPNها محدود و مختص دستگاه هستند، نباید مقادیر SPN را کدبندی سخت کنید. به‌جای آن، از getCurrentDeviceDataSource API برای بازیابی نام سرویس اصلی برای دستگاه فعلی استفاده کنید.

درحالی‌که شمارش گام درون‌دستگاهی به نسخه ۲۰ یا بالاتر افزونه کیت توسعه نرم‌افزار نیاز دارد، میانای برنامه کاربردی getCurrentDeviceDataSource() در Android 14 (سطح ای‌پی‌آی ۳۴) با نسخه ۲۲ یا بالاتر افزونه کیت توسعه نرم‌افزار دردسترس است. برای استفاده از این «میانای برنامه‌سازی کاربردی»، compileSdkExtension را در فایل build.gradle.kts یا build.gradle سطح واحد به 22 یا بالاتر تنظیم کنید:

کاتلین

android {
    compileSdk = 35
    compileSdkExtension = 22
}

شیک

android {
    compileSdk 35
    compileSdkExtension 22
}

میانای برنامه‌سازی کاربردی getCurrentDeviceDataSource() هنوز در کتابخانه Health Connect Jetpack دردسترس نیست. مثال‌های زیر از میانای برنامه‌سازی کاربردی چارچوب Android استفاده می‌کنند که به Executor و OutcomeReceiver پاسخ‌گویی نیاز دارد:

import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.OutcomeReceiver

val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
healthConnectManager?.getCurrentDeviceDataSource(
    context.mainExecutor,
    object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
        override fun onResult(result: DeviceDataSource) {
            val currentDeviceSpn = result.deviceDataOrigin.packageName
        }

        override fun onError(error: HealthConnectException) {
            // Handle error
        }
    }
)

اگر برنامه شما نیاز دارد تعداد گام‌های روی دستگاه را بخواند، یا اگر داده‌های گام را براساس برنامه یا دستگاه منبع تفکیک‌شده نمایش می‌دهد، باید برای گزارش‌هایی که در آن‌ها DataOrigin android یا با SPN دستگاه مطابقت دارد پُرسمان کنید. اگر برنامه شما اسناد استنادی برای داده‌های گام نشان می‌دهد، از metadata.device برای شناسایی دستگاه منبع برای سوابق فردی استفاده کنید. برای مراحل درون‌دستگاهی که با SPN در داده‌های تجمیعی شناسایی شده‌اند، می‌توانید از فراداده‌های دستگاه مثل model یا manufacturer از DeviceDataSource برای اسناد استفاده کنید، یا از برچسب عمومی مثل «تلفن شما» برای مراحل درون‌دستگاهی استفاده کنید.

مثال زیر نحوه خواندن داده‌های تعداد گام‌های انبوهشی درون‌دستگاهی را با فیلتر کردن هم android و هم SPN دستگاه فعلی نشان می‌دهد:

import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.Build
import android.os.OutcomeReceiver
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
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine

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

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

        // 2. Safely fetch the package name only if the API is available
        val currentDeviceSpn = if (isDataSourceApiAvailable && healthConnectManager != null) {
            suspendCancellableCoroutine { continuation ->
                healthConnectManager.getCurrentDeviceDataSource(
                    context.mainExecutor,
                    object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
                        override fun onResult(result: DeviceDataSource) {
                            continuation.resume(result.deviceDataOrigin.packageName)
                        }

                        override fun onError(error: HealthConnectException) {
                            continuation.resumeWithException(error)
                        }
                    }
                )
            }
        } 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 نوشته می‌شوند.
  • اسناد: گام‌هایی که این ویژگی قبل‌از ژوئن ۲۰۲۶ ثبت کرده است به نام بسته 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()
    }
}
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 را دارد که ۱۰۰۰ است. اگر تعداد گزارش‌ها در یک 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 و نسخه‌های بالاتر

    • هیچ محدودیت تاریخی برای خواندن داده‌های خود برنامه وجود ندارد.
    • محدودیت ۳۰ روزه برای برنامه در خواندن داده‌های دیگر.
  • برای Android 13 و نسخه‌های پایین‌تر

    • محدودیت ۳۰ روزه برای خواندن داده‌ها توسط برنامه.

با درخواست اجازه خواندن می‌توان محدودیت‌ها را برداشت.

برای خواندن داده‌های تاریخی، باید نام بسته را به‌عنوان 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
}

خواندن شناسه یکتای دستگاه (UDI)

برای سوابق منشأگرفته از دستگاه‌های پزشکی، برنامه‌های خواندن می‌توانند بخش «شناسه دستگاه» (DI) از «شناسه یکتای دستگاه» (UDI) را از فراداده دستگاه استخراج کنند. برای راهنمایی درباره نحوه پردازش این اطلاعات و تطبیق آن با پایگاه‌های داده نظارتی، به راهنمای فراداده مراجعه کنید.

خواندن داده‌های قدیمی‌تر از ۳۰ روز

به‌طور پیش‌فرض، همه برنامه‌ها می‌توانند داده‌های Health Connect را تا ۳۰ روز قبل‌از زمانی که اولین اجازه اعطا شده است بخوانند.

اگر نیاز دارید اجازه‌های خواندن را فراتر از هریک از محدودیت‌های پیش‌فرض گسترش دهید، PERMISSION_READ_HEALTH_DATA_HISTORY را درخواست کنید. درغیراین‌صورت، بدون این اجازه، تلاش برای خواندن سوابق قدیمی‌تر از ۳۰ روز منجر به خطا می‌شود.

سابقه اجازه‌های برنامه حذف‌شده

اگر کاربری برنامه شما را حذف کند، همه اجازه‌ها، ازجمله اجازه سابقه، لغو می‌شود. اگر کاربر برنامه‌تان را دوباره نصب کند و اجازه را دوباره اعطا کند، همان محدودیت‌های پیش‌فرض اعمال می‌شود و برنامه شما می‌تواند داده‌های Health Connect را تا ۳۰ روز قبل‌از آن تاریخ جدید بخواند.

برای مثال، فرض کنید کاربر برنامه شما را در ۱۰ مه ۲۰۲۳ حذف کند و سپس برنامه را در ۱۵ مه ۲۰۲۳ بازنصب کند و اجازه‌های خواندن را اعطا کند. از این پس، اولین تاریخی که برنامه‌تان می‌تواند به‌طور پیش‌فرض داده‌ها را از آن بخواند ۱۵ آوریل ۲۰۲۳ است.

مدیریت استثناها

‫Health Connect هنگام مواجهه با مشکل، استثناهای استاندارد برای عملیات CRUD ایجاد می‌کند. برنامه شما باید هریک از این استثناها را به‌صورت مناسب دریافت و مدیریت کند.

هر روش در HealthConnectClient استثناهایی را که ممکن است ایجاد شود فهرست می‌کند. به‌طورکلی، برنامه شما باید استثناهای زیر را مدیریت کند:

جدول ۱: استثناهای Health Connect و روال‌های مطلوب توصیه‌شده
استثنا شرح روال مطلوب توصیه‌شده
IllegalStateException یکی از سناریوهای زیر رخ داده است:

  • سرویس Health Connect دردسترس نیست.
  • درخواست ساختار معتبری ندارد. برای مثال، درخواست تجمیعی در باکت‌های دوره‌ای که در آن از شیء Instant برای timeRangeFilter استفاده می‌شود.

ابتدا مشکلات احتمالی ورودی‌ها را برطرف کنید و سپس درخواست دهید. ترجیحاً، مقادیر را به متغیرها اختصاص دهید یا از آن‌ها به‌عنوان پارامتر در یک تابع سفارشی استفاده کنید، به‌جای اینکه مستقیماً در درخواست‌هایتان از آن‌ها استفاده کنید تا بتوانید استراتژی‌های مدیریت خطا را اعمال کنید.
IOException هنگام خواندن و نوشتن داده‌ها از دیسک، مشکلاتی پیش آمد. برای جلوگیری از این مشکل، در اینجا چند پیشنهاد ارائه شده است:

  • از ورودی‌های کاربر پشتیبان‌گیری کنید.
  • بتواند از عهده هر مشکلی که درطول عملیات نوشتن انبوه پیش می‌آید برآید. برای مثال، مطمئن شوید که فرایند از مشکل عبور می‌کند و عملیات باقی‌مانده را انجام می‌دهد.
  • برای مدیریت مشکلات درخواست، از استراتژی‌های تلاش مجدد و بازگشت استفاده کنید.

RemoteException خطاهایی در سرویس زیربنایی که کیت توسعه نرم‌افزار به آن متصل می‌شود یا در برقراری ارتباط با آن رخ داده است.

برای مثال، برنامه شما درحال تلاش برای حذف کردن گزارشی با uid داده‌شده است. بااین‌حال، استثنا پس‌از آنکه برنامه با بررسی سرویس زیربنایی متوجه می‌شود که سابقه وجود ندارد، ایجاد می‌شود.
برای جلوگیری از این مشکل، در اینجا چند پیشنهاد ارائه شده است:

  • همگام‌سازی‌های منظم بین مخزن داده برنامه و Health Connect انجام دهید.
  • برای مدیریت مشکلات درخواست، از استراتژی‌های تلاش مجدد و بازگشت استفاده کنید.

SecurityException وقتی درخواست‌ها به اجازه‌هایی نیاز دارند که اعطا نشده‌اند، مشکلاتی پیش می‌آید. برای جلوگیری از این اتفاق، مطمئن شوید که استفاده از انواع داده‌های Health Connect را برای برنامه منتشرشده‌تان اعلام کرده‌اید. همچنین، باید اجازه‌های Health Connect را در فایل مانیفست و در فعالیتتان اعلام کنید.