یک تمرین را با ExerciseClient ضبط کنید

Health Services از طریق ExerciseClient پشتیبانی درجه یک برای برنامه های تمرینی ارائه می دهد. با ExerciseClient ، برنامه شما می‌تواند زمان انجام تمرین را کنترل کند، اهداف تمرینی را اضافه کند و درباره وضعیت ورزش، رویدادهای ورزشی یا سایر معیارهای مورد نظر به‌روزرسانی دریافت کند. برای کسب اطلاعات بیشتر، لیست کامل انواع ورزش را که Health Services پشتیبانی می کند، ببینید.

نمونه تمرین را در GitHub ببینید.

وابستگی ها را اضافه کنید

برای افزودن وابستگی به خدمات سلامت، باید مخزن Google Maven را به پروژه خود اضافه کنید. برای اطلاعات بیشتر، به مخزن Maven Google مراجعه کنید.

سپس، در فایل build.gradle در سطح ماژول، وابستگی زیر را اضافه کنید:

شیار

dependencies {
    implementation "androidx.health:health-services-client:1.1.0-alpha03"
}

کاتلین

dependencies {
    implementation("androidx.health:health-services-client:1.1.0-alpha03")
}

ساختار برنامه

هنگام ساختن یک برنامه ورزشی با خدمات بهداشتی، از ساختار برنامه زیر استفاده کنید:

هنگام آماده شدن برای تمرین و در طول تمرین، ممکن است فعالیت شما به دلایل مختلفی متوقف شود. کاربر ممکن است به برنامه دیگری تغییر مکان دهد یا به صفحه ساعت بازگردد. ممکن است سیستم چیزی در بالای فعالیت شما نمایش دهد، یا ممکن است صفحه بعد از مدتی عدم فعالیت خاموش شود. از یک ForegroundService به طور مداوم در حال اجرا همراه با ExerciseClient استفاده کنید تا از عملکرد صحیح برای کل تمرین اطمینان حاصل کنید.

استفاده از ForegroundService به شما امکان می‌دهد از 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() و برای فعالیت تمرینی شماست. با این حال، نمایشگر را در حین تمرین در حالت محیطی به‌روزرسانی نکنید: این به این دلیل است که خدمات بهداشتی زمانی که صفحه دستگاه در حالت محیطی است برای صرفه‌جویی در مصرف انرژی، داده‌های تمرین را دسته‌بندی می‌کند، بنابراین ممکن است اطلاعات نمایش داده‌شده جدید نباشد. در طول تمرین، داده هایی را نشان دهید که برای کاربر منطقی است، یا اطلاعات به روز یا یک صفحه خالی نمایش داده می شود.

بررسی قابلیت ها

هر 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

برای به روز رسانی وضعیت ورزش ثبت نام کنید

به روز رسانی های تمرین به شنونده تحویل داده می شود. برنامه شما می‌تواند هر بار فقط یک شنونده را ثبت کند. همانطور که در مثال زیر نشان داده شده است، قبل از شروع تمرین شنونده خود را تنظیم کنید. شنونده شما فقط به‌روزرسانی‌هایی را درباره تمرین‌هایی که برنامه‌تان متعلق به شماست دریافت می‌کند.

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)

طول عمر تمرین را مدیریت کنید

Health Services حداکثر از یک تمرین در هر زمان در همه برنامه‌های روی دستگاه پشتیبانی می‌کند. اگر تمرینی در حال ردیابی باشد و برنامه دیگری شروع به ردیابی یک تمرین جدید کند، اولین تمرین خاتمه می یابد.

قبل از شروع ورزش، موارد زیر را انجام دهید:

  • بررسی کنید که آیا یک تمرین از قبل ردیابی شده است یا خیر، و مطابق با آن واکنش نشان دهید. به عنوان مثال، قبل از لغو تمرین قبلی و شروع به ردیابی تمرین جدید، از کاربر تأیید بخواهید.

مثال زیر نحوه بررسی یک تمرین موجود با 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 خود مشخص کنید.
  • بررسی کنید که کاربر مجوزهای لازم را داده است. برای اطلاعات بیشتر، به درخواست مجوزهای برنامه مراجعه کنید. اگر مجوزهای لازم قبلاً داده نشده باشد، خدمات درمانی درخواست را رد می کند.

برای اطلاعات مکان، مراحل اضافی زیر را انجام دهید:

برای یک تمرین آماده شوید

برخی از حسگرها، مانند GPS یا ضربان قلب، ممکن است مدت کوتاهی طول بکشد تا گرم شوند، یا ممکن است کاربر بخواهد قبل از شروع تمرین، اطلاعات خود را ببیند. روش اختیاری prepareExerciseAsync() به این حسگرها اجازه می‌دهد تا بدون شروع تایمر تمرین گرم شوند و داده‌ها دریافت شوند. activeDuration تحت تأثیر این زمان آماده سازی قرار نمی گیرد.

قبل از برقراری تماس برای prepareExerciseAsync() ، موارد زیر را بررسی کنید:

  • تنظیم موقعیت مکانی در سطح پلت فرم را بررسی کنید. کاربر این تنظیم را در منوی تنظیمات اصلی کنترل می کند. با بررسی مجوزهای سطح برنامه متفاوت است.

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

  • تأیید کنید که برنامه شما دارای مجوزهای زمان اجرا برای حسگرهای بدن، تشخیص فعالیت و مکان دقیق است. برای مجوزهای از دست رفته، از کاربر بخواهید مجوزهای زمان اجرا را دریافت کند و زمینه کافی را فراهم کند. اگر کاربر مجوز خاصی اعطا نمی کند، انواع داده های مرتبط با آن مجوز را از فراخوانی prepareExerciseAsync() حذف کنید. اگر مجوزهای حسگر بدن و موقعیت مکانی داده نشده است، با prepareExerciseAsync() تماس نگیرید، زیرا تماس آماده به طور خاص برای به دست آوردن ضربان قلب ثابت یا اصلاح GPS قبل از شروع تمرین است. این برنامه همچنان می‌تواند فاصله، سرعت، سرعت و سایر معیارهایی را که به آن مجوزها نیاز ندارند، بر اساس گام دریافت کند.

برای اطمینان از موفقیت آمیز بودن فراخوانی برای prepareExerciseAsync() موارد زیر را انجام دهید:

  • از AmbientLifecycleObserver برای فعالیت قبل از تمرین که حاوی تماس آماده است استفاده کنید.
  • از سرویس پیش زمینه خود prepareExerciseAsync() فراخوانی کنید. اگر در سرویس نباشد و با چرخه حیات فعالیت مرتبط باشد، ممکن است آماده سازی حسگر به طور غیر ضروری از بین برود.
  • برای خاموش کردن حسگرها و کاهش مصرف انرژی اگر کاربر از فعالیت قبل از تمرین دور شد، endExercise() فراخوانی کنید.

مثال زیر نحوه فراخوانی prepareExerciseAsync() نشان می دهد:

val warmUpConfig = WarmUpConfig(
    ExerciseType.RUNNING,
    setOf(
        DataType.HEART_RATE_BPM,
        DataType.LOCATION
    )
)
// Only necessary to call prepareExerciseAsync if body sensor 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 را نشان می‌دهد که هنگام درخواست داده‌های مکان باید درست باشد. با این حال، استفاده از GPS می تواند به سایر معیارها نیز کمک کند. اگر ExerciseConfig فاصله را به عنوان DataType مشخص کند، به طور پیش‌فرض از مراحل برای تخمین فاصله استفاده می‌کند. با فعال کردن اختیاری GPS، می‌توان از اطلاعات موقعیت مکانی برای تخمین مسافت استفاده کرد.

تمرین را مکث کنید، از سر بگیرید و پایان دهید

می‌توانید با استفاده از روش‌های مناسب، مانند pauseExerciseAsync() یا endExerciseAsync() تمرینات را مکث، از سرگیری و پایان دهید.

از حالت ExerciseUpdate به عنوان منبع حقیقت استفاده کنید. هنگامی که فراخوانی به pauseExerciseAsync() برمی‌گردد، تمرین متوقف شده در نظر گرفته نمی‌شود، بلکه زمانی که آن حالت در پیام ExerciseUpdate منعکس می‌شود. وقتی صحبت از حالت های رابط کاربری به میان می آید، توجه به این موضوع بسیار مهم است. اگر کاربر pause را فشار داد، دکمه مکث را غیرفعال کنید و pauseExerciseAsync() را در Health Services فراخوانی کنید. منتظر بمانید تا Health Services با استفاده از 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 با استفاده از معادله زیر محاسبه کرد:

(اکنون() - checkpoint.time) + checkpoint.activeDuration

این دلتای کوچک بین مدت زمان فعال محاسبه شده در MCU و رسیدن به برنامه را به حساب می آورد. این می تواند برای ایجاد یک کرونومتر در برنامه استفاده شود و به اطمینان حاصل شود که تایمر برنامه کاملاً با زمان در خدمات بهداشتی و MCU هماهنگ است.

اگر تمرین متوقف شود، برنامه منتظر می‌ماند تا تایمر را در رابط کاربری مجدداً راه‌اندازی کند تا زمانی که زمان محاسبه‌شده از آنچه که رابط کاربری در حال حاضر نشان می‌دهد گذشته باشد. این به این دلیل است که سیگنال مکث با کمی تاخیر به خدمات بهداشتی و MCU می رسد. برای مثال، اگر برنامه در t=10 ثانیه متوقف شود، ممکن است Health Services به‌روزرسانی PAUSED را تا t=10.2 ثانیه به برنامه تحویل ندهد.

با داده های ExerciseClient کار کنید

معیارهای مربوط به انواع داده هایی که برنامه شما برای آنها ثبت کرده است در پیام های ExerciseUpdate ارائه می شود.

پردازشگر پیام‌ها را فقط در حالت بیدار یا زمانی که به حداکثر دوره گزارش‌دهی رسیده است، مانند هر 150 ثانیه ارسال می‌کند. برای پیشبرد کرنومتر با activeDuration به فرکانس ExerciseUpdate تکیه نکنید. برای نمونه ای از نحوه پیاده سازی یک کرنومتر مستقل ، نمونه تمرین را در 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 برای تعیین اینکه آیا هر نقطه داده از دقت کافی برای برنامه شما برخوردار است استفاده کنید.

ذخیره و آپلود داده ها

از اتاق برای تداوم داده های ارائه شده از خدمات بهداشتی استفاده کنید. آپلود داده ها در پایان تمرین با استفاده از مکانیزمی مانند Work Manager انجام می شود. این تضمین می‌کند که تماس‌های شبکه برای آپلود داده‌ها تا پایان تمرین به تعویق می‌افتد، مصرف برق را در طول تمرین به حداقل می‌رساند و کار را ساده می‌کند.

چک لیست ادغام

قبل از انتشار برنامه خود که از ExerciseClient از Health Services استفاده می کند، به چک لیست زیر مراجعه کنید تا مطمئن شوید تجربه کاربری شما از برخی مشکلات رایج جلوگیری می کند. تایید کنید که:

  • برنامه شما هر بار که برنامه اجرا می شود ، قابلیت های نوع تمرین و قابلیت های دستگاه را بررسی می کند . به این ترتیب، می توانید تشخیص دهید که یک دستگاه یا تمرین خاص از یکی از انواع داده های مورد نیاز برنامه شما پشتیبانی نمی کند.
  • شما مجوزهای لازم را درخواست و حفظ می کنید و آنها را در فایل مانیفست خود مشخص می کنید. قبل از فراخوانی prepareExerciseAsync() ، برنامه شما تأیید می کند که مجوزهای زمان اجرا داده شده است.
  • برنامه شما از getCurrentExerciseInfoAsync() برای رسیدگی به موارد زیر استفاده می کند:
    • یک تمرین از قبل در حال پیگیری است و برنامه شما تمرین قبلی را لغو می کند.
    • برنامه دیگری تمرین شما را خاتمه داده است. این ممکن است زمانی اتفاق بیفتد که کاربر دوباره برنامه را باز می کند، با پیامی مواجه می شود که توضیح می دهد تمرین متوقف شده است زیرا برنامه دیگری کنترل را به دست گرفته است.
  • اگر از داده های LOCATION استفاده می کنید:
    • برنامه شما یک ForegroundService با foregroundServiceType مربوطه را در طول مدت تمرین (از جمله تماس آماده) حفظ می کند.
    • بررسی کنید که GPS با استفاده از isProviderEnabled(LocationManager.GPS_PROVIDER) در دستگاه فعال باشد و در صورت لزوم از کاربر می خواهد تنظیمات مکان را باز کند.
    • برای موارد استفاده سخت، که دریافت داده‌های مکان با تأخیر کم اهمیت زیادی دارد، یکپارچه‌سازی ارائه‌دهنده مکان فیوز شده (FLP) و استفاده از داده‌های آن به‌عنوان یک اصلاح مکان اولیه را در نظر بگیرید. هنگامی که اطلاعات مکان پایدارتری از خدمات بهداشتی در دسترس است، به جای FLP از آن استفاده کنید.
  • اگر برنامه شما به آپلود داده نیاز دارد، هر تماس شبکه برای آپلود داده تا پایان تمرین به تعویق می‌افتد. در غیر این صورت، در طول تمرین، برنامه شما هر گونه تماس شبکه ضروری را به ندرت انجام می دهد.
{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% endverbatim %}،

Health Services از طریق ExerciseClient پشتیبانی درجه یک برای برنامه های تمرینی ارائه می دهد. با ExerciseClient ، برنامه شما می‌تواند زمان انجام تمرین را کنترل کند، اهداف تمرینی را اضافه کند و درباره وضعیت ورزش، رویدادهای ورزشی یا سایر معیارهای مورد نظر به‌روزرسانی دریافت کند. برای کسب اطلاعات بیشتر، لیست کامل انواع ورزش را که Health Services پشتیبانی می کند، ببینید.

نمونه تمرین را در GitHub ببینید.

وابستگی ها را اضافه کنید

برای افزودن وابستگی به خدمات سلامت، باید مخزن Google Maven را به پروژه خود اضافه کنید. برای اطلاعات بیشتر، به مخزن Maven Google مراجعه کنید.

سپس، در فایل build.gradle در سطح ماژول، وابستگی زیر را اضافه کنید:

شیار

dependencies {
    implementation "androidx.health:health-services-client:1.1.0-alpha03"
}

کاتلین

dependencies {
    implementation("androidx.health:health-services-client:1.1.0-alpha03")
}

ساختار برنامه

هنگام ساختن یک برنامه ورزشی با خدمات بهداشتی، از ساختار برنامه زیر استفاده کنید:

هنگام آماده شدن برای تمرین و در طول تمرین، ممکن است فعالیت شما به دلایل مختلفی متوقف شود. کاربر ممکن است به برنامه دیگری تغییر مکان دهد یا به صفحه ساعت بازگردد. ممکن است سیستم چیزی در بالای فعالیت شما نمایش دهد، یا ممکن است صفحه بعد از مدتی عدم فعالیت خاموش شود. از یک ForegroundService به طور مداوم در حال اجرا همراه با ExerciseClient استفاده کنید تا از عملکرد صحیح برای کل تمرین اطمینان حاصل کنید.

استفاده از ForegroundService به شما امکان می‌دهد از 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() و برای فعالیت تمرینی شماست. با این حال، نمایشگر را در حین تمرین در حالت محیطی به‌روزرسانی نکنید: این به این دلیل است که خدمات بهداشتی زمانی که صفحه دستگاه در حالت محیطی است برای صرفه‌جویی در مصرف انرژی، داده‌های تمرین را دسته‌بندی می‌کند، بنابراین ممکن است اطلاعات نمایش داده‌شده جدید نباشد. در طول تمرین، داده هایی را نشان دهید که برای کاربر منطقی است، یا اطلاعات به روز یا یک صفحه خالی نمایش داده می شود.

بررسی قابلیت ها

هر 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

برای به روز رسانی وضعیت ورزش ثبت نام کنید

به روز رسانی های تمرین به شنونده تحویل داده می شود. برنامه شما می‌تواند هر بار فقط یک شنونده را ثبت کند. همانطور که در مثال زیر نشان داده شده است، قبل از شروع تمرین شنونده خود را تنظیم کنید. شنونده شما فقط به‌روزرسانی‌هایی را درباره تمرین‌هایی که برنامه‌تان متعلق به شماست دریافت می‌کند.

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)

طول عمر تمرین را مدیریت کنید

Health Services حداکثر از یک تمرین در هر زمان در همه برنامه‌های روی دستگاه پشتیبانی می‌کند. اگر تمرینی در حال ردیابی باشد و برنامه دیگری شروع به ردیابی یک تمرین جدید کند، اولین تمرین خاتمه می یابد.

قبل از شروع ورزش، موارد زیر را انجام دهید:

  • بررسی کنید که آیا یک تمرین از قبل ردیابی شده است یا خیر، و مطابق با آن واکنش نشان دهید. به عنوان مثال، قبل از لغو تمرین قبلی و شروع به ردیابی تمرین جدید، از کاربر تأیید بخواهید.

مثال زیر نحوه بررسی یک تمرین موجود با 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 خود مشخص کنید.
  • بررسی کنید که کاربر مجوزهای لازم را داده است. برای اطلاعات بیشتر، به درخواست مجوزهای برنامه مراجعه کنید. اگر مجوزهای لازم قبلاً داده نشده باشد، خدمات درمانی درخواست را رد می کند.

برای اطلاعات مکان، مراحل اضافی زیر را انجام دهید:

برای یک تمرین آماده شوید

برخی از حسگرها، مانند GPS یا ضربان قلب، ممکن است مدت کوتاهی طول بکشد تا گرم شوند، یا ممکن است کاربر بخواهد قبل از شروع تمرین، اطلاعات خود را ببیند. روش اختیاری prepareExerciseAsync() به این حسگرها اجازه می‌دهد تا بدون شروع تایمر تمرین گرم شوند و داده‌ها دریافت شوند. activeDuration تحت تأثیر این زمان آماده سازی قرار نمی گیرد.

قبل از برقراری تماس برای prepareExerciseAsync() ، موارد زیر را بررسی کنید:

  • تنظیم موقعیت مکانی در سطح پلت فرم را بررسی کنید. کاربر این تنظیم را در منوی تنظیمات اصلی کنترل می کند. با بررسی مجوزهای سطح برنامه متفاوت است.

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

  • تأیید کنید که برنامه شما دارای مجوزهای زمان اجرا برای حسگرهای بدن، تشخیص فعالیت و مکان دقیق است. برای مجوزهای از دست رفته، از کاربر بخواهید مجوزهای زمان اجرا را دریافت کند و زمینه کافی را فراهم کند. اگر کاربر مجوز خاصی اعطا نمی کند، انواع داده های مرتبط با آن مجوز را از فراخوانی prepareExerciseAsync() حذف کنید. اگر مجوزهای حسگر بدن و موقعیت مکانی داده نشده است، با prepareExerciseAsync() تماس نگیرید، زیرا تماس آماده به طور خاص برای به دست آوردن ضربان قلب ثابت یا اصلاح GPS قبل از شروع تمرین است. این برنامه همچنان می‌تواند فاصله، سرعت، سرعت و سایر معیارهایی را که به آن مجوزها نیاز ندارند، بر اساس گام دریافت کند.

برای اطمینان از موفقیت آمیز بودن فراخوانی برای prepareExerciseAsync() موارد زیر را انجام دهید:

  • از AmbientLifecycleObserver برای فعالیت قبل از تمرین که حاوی تماس آماده است استفاده کنید.
  • از سرویس پیش زمینه خود prepareExerciseAsync() فراخوانی کنید. اگر در سرویس نباشد و با چرخه حیات فعالیت مرتبط باشد، ممکن است آماده سازی حسگر به طور غیر ضروری از بین برود.
  • برای خاموش کردن حسگرها و کاهش مصرف انرژی اگر کاربر از فعالیت قبل از تمرین دور شد، endExercise() فراخوانی کنید.

مثال زیر نحوه فراخوانی prepareExerciseAsync() نشان می دهد:

val warmUpConfig = WarmUpConfig(
    ExerciseType.RUNNING,
    setOf(
        DataType.HEART_RATE_BPM,
        DataType.LOCATION
    )
)
// Only necessary to call prepareExerciseAsync if body sensor 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 را نشان می‌دهد که هنگام درخواست داده‌های مکان باید درست باشد. با این حال، استفاده از GPS می تواند به سایر معیارها نیز کمک کند. اگر ExerciseConfig فاصله را به عنوان DataType مشخص کند، به طور پیش‌فرض از مراحل برای تخمین فاصله استفاده می‌کند. با فعال کردن اختیاری GPS، می‌توان از اطلاعات موقعیت مکانی برای تخمین مسافت استفاده کرد.

تمرین را مکث کنید، از سر بگیرید و پایان دهید

می‌توانید با استفاده از روش‌های مناسب، مانند pauseExerciseAsync() یا endExerciseAsync() تمرینات را مکث، از سرگیری و پایان دهید.

از حالت ExerciseUpdate به عنوان منبع حقیقت استفاده کنید. هنگامی که فراخوانی به pauseExerciseAsync() برمی‌گردد، تمرین متوقف شده در نظر گرفته نمی‌شود، بلکه زمانی که آن حالت در پیام ExerciseUpdate منعکس می‌شود. وقتی صحبت از حالت های رابط کاربری به میان می آید، توجه به این موضوع بسیار مهم است. اگر کاربر pause را فشار داد، دکمه مکث را غیرفعال کنید و pauseExerciseAsync() را در Health Services فراخوانی کنید. منتظر بمانید تا Health Services با استفاده از 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 با استفاده از معادله زیر محاسبه کرد:

(اکنون() - checkpoint.time) + checkpoint.activeDuration

این دلتای کوچک بین مدت زمان فعال محاسبه شده در MCU و رسیدن به برنامه را به حساب می آورد. این می تواند برای ایجاد یک کرونومتر در برنامه استفاده شود و به اطمینان حاصل شود که تایمر برنامه کاملاً با زمان در خدمات بهداشتی و MCU هماهنگ است.

اگر تمرین متوقف شود، برنامه منتظر می‌ماند تا تایمر را در رابط کاربری مجدداً راه‌اندازی کند تا زمانی که زمان محاسبه‌شده از آنچه که رابط کاربری در حال حاضر نشان می‌دهد گذشته باشد. این به این دلیل است که سیگنال مکث با کمی تاخیر به خدمات بهداشتی و MCU می رسد. برای مثال، اگر برنامه در t=10 ثانیه متوقف شود، ممکن است Health Services به‌روزرسانی PAUSED را تا t=10.2 ثانیه به برنامه تحویل ندهد.

با داده های ExerciseClient کار کنید

معیارهای مربوط به انواع داده هایی که برنامه شما برای آنها ثبت کرده است در پیام های ExerciseUpdate ارائه می شود.

پردازشگر پیام‌ها را فقط در حالت بیدار یا زمانی که به حداکثر دوره گزارش‌گیری رسیده است، مانند هر 150 ثانیه ارسال می‌کند. برای پیشبرد کرنومتر با activeDuration به فرکانس ExerciseUpdate تکیه نکنید. برای نمونه ای از نحوه پیاده سازی یک کرنومتر مستقل ، نمونه تمرین را در 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. برای پاک کردن mode customized BatchingMode و بازگشت به رفتار پیش فرض ، یک مجموعه خالی را به exerciseClient.overrideBatchingModesForActiveExercise() .

مهر زمانی

نقطه به زمان هر نقطه داده مدت زمان بوت شدن دستگاه را نشان می دهد. برای تبدیل این کار به یک Timestamp ، موارد زیر را انجام دهید:

val bootInstant =
    Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())

این مقدار می تواند برای هر نقطه داده با getStartInstant() یا getEndInstant() استفاده شود.

دقت داده ها

برخی از انواع داده ها می توانند دارای اطلاعات صحت مرتبط با هر نقطه داده باشند. این در خاصیت accuracy نشان داده شده است.

کلاسهای HrAccuracy و LocationAccuracy می توانند به ترتیب برای HEART_RATE_BPM و انواع داده های LOCATION جمع شوند. در صورت وجود ، از ویژگی accuracy استفاده کنید تا مشخص کنید که آیا هر نقطه داده از دقت کافی برای برنامه شما برخوردار است یا خیر.

داده ها را ذخیره و بارگذاری کنید

از اتاق برای ادامه داده های ارائه شده از خدمات درمانی استفاده کنید. بارگذاری داده ها در پایان تمرین با استفاده از مکانیسمی مانند مدیر کار اتفاق می افتد. این تضمین می کند که تماس های شبکه برای بارگذاری داده ها تا پایان تمرین به تعویق می افتد ، به حداقل رساندن مصرف برق در طول تمرین و ساده سازی کار.

لیست چک

قبل از انتشار برنامه خود که ExerciseClient خدمات بهداشتی استفاده می کند ، با لیست چک زیر مشورت کنید تا اطمینان حاصل کنید که تجربه کاربر شما از برخی موارد مشترک جلوگیری می کند. تایید کنید که:

  • برنامه شما قابلیت های نوع تمرین و قابلیت های دستگاه را هر بار که برنامه اجرا می شود ، بررسی می کند . به این ترتیب ، شما می توانید تشخیص دهید که یک دستگاه خاص یا ورزش از یکی از انواع داده های برنامه شما پشتیبانی نمی کند.
  • شما مجوزهای لازم را درخواست و نگهداری می کنید و این موارد را در پرونده مانیفست خود مشخص می کنید. قبل از تماس با prepareExerciseAsync() ، برنامه شما تأیید می کند که مجوزهای زمان اجرا اعطا می شود.
  • برنامه شما از getCurrentExerciseInfoAsync() برای رسیدگی به مواردی که در آن استفاده می کند استفاده می کند:
    • یک تمرین در حال حاضر ردیابی می شود و برنامه شما از تمرین قبلی غلبه می کند.
    • برنامه دیگری تمرین شما را خاتمه داده است. این ممکن است زمانی اتفاق بیفتد که کاربر دوباره برنامه را باز کند ، آنها با پیامی روبرو می شوند که توضیح می دهد که این تمرین متوقف شده است زیرا برنامه دیگری به دست گرفته است.
  • اگر از داده های LOCATION استفاده می کنید:
    • برنامه شما در طول مدت تمرین (از جمله آماده سازی آماده سازی) یک ForegroundService با foregroundServiceType مربوطه را حفظ می کند.
    • بررسی کنید که GPS با استفاده از isProviderEnabled(LocationManager.GPS_PROVIDER) در دستگاه فعال شده است و کاربر را وادار می کند در صورت لزوم تنظیمات مکان را باز کند.
    • برای موارد مورد نیاز استفاده ، که دریافت داده های مکان با تأخیر کم از اهمیت زیادی برخوردار است ، ادغام ارائه دهنده مکان ذوب شده (FLP) را در نظر بگیرید و از داده های آن به عنوان یک مکان اولیه استفاده کنید. هنگامی که اطلاعات مکان پایدارتر از خدمات درمانی در دسترس است ، به جای FLP از آن استفاده کنید.
  • اگر برنامه شما به بارگذاری داده نیاز دارد ، هرگونه تماس شبکه برای بارگذاری داده ها تا پایان تمرین به تعویق می افتد. در غیر این صورت ، در طول تمرین ، برنامه شما هرگونه تماس با شبکه لازم را به طور کم مصرف می کند.
{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}