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

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

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

Health Connect به برنامه‌ها اجازه می‌دهد تا داده‌ها را از پایگاه داده بخوانند، چه در پیش‌زمینه و چه در پس‌زمینه:

  • خواندن داده‌ها در پیش‌زمینه : معمولاً می‌توانید داده‌ها را از Health Connect بخوانید، زمانی که برنامه شما در پیش‌زمینه است. در این موارد، می‌توانید از یک سرویس پیش‌زمینه برای اجرای این عملیات استفاده کنید، در صورتی که کاربر یا سیستم، برنامه شما را در حین عملیات خواندن در پس‌زمینه قرار دهد.

  • خواندن پس‌زمینه : با درخواست مجوز اضافی از کاربر، می‌توانید داده‌ها را پس از قرار دادن برنامه در پس‌زمینه توسط کاربر یا سیستم، بخوانید. مثال کامل خواندن پس‌زمینه را ببینید.

نوع داده Steps در 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
}

مراحل موبایل را بخوانید

با اندروید ۱۴ (سطح API ۳۴) و افزونه SDK نسخه ۲۰ یا بالاتر، Health Connect امکان شمارش گام‌ها را روی دستگاه فراهم می‌کند. اگر به هر برنامه‌ای مجوز READ_STEPS اعطا شده باشد، Health Connect شروع به ثبت گام‌ها از دستگاه مبتنی بر اندروید می‌کند و کاربران می‌توانند داده‌های گام‌ها را که به طور خودکار به ورودی‌های Health Connect Steps اضافه می‌شوند، مشاهده کنند.

برای بررسی اینکه آیا شمارش گام روی دستگاه در دسترس است، تأیید کنید که دستگاه از اندروید ۱۴ (سطح API ۳۴) استفاده می‌کند و حداقل نسخه ۲۰ افزونه SDK را دارد:

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 های متفاوتی را برای داده‌های مرحله‌ای روی دستگاه مشاهده می‌کنند.

پرس و جو برای مراحل روی دستگاه

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

در حالی که شمارش گام روی دستگاه به افزونه SDK نسخه 20 یا بالاتر نیاز دارد، API getCurrentDeviceDataSource() در اندروید 14 (سطح API 34) با افزونه SDK نسخه 11 یا بالاتر در دسترس است.

API getCurrentDeviceDataSource() هنوز در کتابخانه Health Connect Jetpack موجود نیست. مثال‌های زیر به جای آن از API فریم‌ورک اندروید استفاده می‌کنند:

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 نوشته می‌شوند.
  • انتساب : مراحلی که توسط این ویژگی قبل از ژوئن ۲۰۲۶ ثبت شده‌اند، به نام بسته 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 برابر با ۱۰۰۰ است. اگر تعداد رکوردهای موجود در یک 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 دارد.

برخی محدودیت‌های خواندن اعمال می‌شود:

  • برای اندروید ۱۴ و بالاتر

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

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

این محدودیت‌ها را می‌توان با درخواست مجوز خواندن (Read permission) حذف کرد.

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

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

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

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

تاریخچه مجوزها برای یک برنامه حذف شده

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

برای مثال، فرض کنید کاربر برنامه شما را در 10 می 2023 حذف می‌کند و سپس در 15 می 2023 برنامه را دوباره نصب می‌کند و مجوزهای خواندن را اعطا می‌کند. زودترین تاریخی که برنامه شما اکنون می‌تواند داده‌ها را به طور پیش‌فرض از آن بخواند ، 15 آوریل 2023 است.

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

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

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

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

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

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

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

RemoteException خطاهایی در داخل یا در ارتباط با سرویس اصلی که SDK به آن متصل می‌شود، رخ داده است.

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

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

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